diff --git a/crates/render/src/fx.rs b/crates/render/src/fx.rs index ff3aef62e..7bdeceadc 100644 --- a/crates/render/src/fx.rs +++ b/crates/render/src/fx.rs @@ -2130,6 +2130,7 @@ impl Renderer { if reads.images { let o = f.opts; (doc.images, self.dim_images.map(f32::to_bits), o.smooth_images, o.image_mips, o.trace_views, o.progressive_placed).hash(&mut h); + o.trace_source.as_deref().hash(&mut h); } if reads.brushes { self.brushes.library_key(f.doc, self.stamp).hash(&mut h); diff --git a/crates/render/src/lib.rs b/crates/render/src/lib.rs index f87832fb4..0c0f8c7c1 100644 --- a/crates/render/src/lib.rs +++ b/crates/render/src/lib.rs @@ -159,6 +159,10 @@ pub struct RenderOptions { /// Screen view: Image Trace objects draw as their View asks (outlines, the source image…); /// off, they draw their tracing result, as exports and printing do. pub trace_views: bool, + /// Screen view with `trace_views` on: the Image Trace objects drawn with the Source Image view + /// for this render only, whatever View they have (the Image Trace panel's eye button, while it + /// is held down). + pub trace_source: Option>>, /// Images are sampled smoothly when scaled or rotated; off, each pixel takes its nearest image /// pixel (Pixel Preview with File Handling › Display Bitmaps as Anti-aliased Images off). pub smooth_images: bool, @@ -266,6 +270,7 @@ impl Default for RenderOptions { anti_alias: AntiAlias::Art, progressive_placed: false, trace_views: false, + trace_source: None, smooth_images: true, isolated: None, precise: false, @@ -1301,8 +1306,8 @@ impl Renderer { /// What `n` draws inside its transparency group (`knockout`: its children knock each other out). fn draw_content(&mut self, ctx: &mut RenderContext, f: &Frame, n: &Node, knockout: bool) { match &n.kind { - NodeKind::Group { children, clip: false } if f.opts.trace_views && !f.opts.outline && n.trace_view() != TraceView::Result => { - self.draw_trace_view(ctx, f, children, n.trace_view(), knockout) + NodeKind::Group { children, clip: false } if f.opts.trace_views && !f.opts.outline && Self::trace_view(f, n) != TraceView::Result => { + self.draw_trace_view(ctx, f, children, Self::trace_view(f, n), knockout) } NodeKind::Layer { children, clip: false, .. } | NodeKind::Group { children, clip: false } => { self.draw_children(ctx, f, children, knockout) @@ -1429,6 +1434,15 @@ impl Renderer { self.live_root = outer; } + /// The View `n` draws with on screen: Source Image when it is an Image Trace object listed in + /// [`RenderOptions::trace_source`], else its own. + fn trace_view(f: &Frame, n: &Node) -> TraceView { + match &f.opts.trace_source { + Some(ids) if n.extras().trace.is_some() && ids.contains(&n.id) => TraceView::Source, + _ => n.trace_view(), + } + } + /// An Image Trace object's `children` (the hidden source image, then the traced shapes) as /// `view` shows them on screen. fn draw_trace_view(&mut self, ctx: &mut RenderContext, f: &Frame, children: &[Arc], view: TraceView, knockout: bool) { diff --git a/crates/render/src/tests.rs b/crates/render/src/tests.rs index bc5ede38b..fb2207e56 100644 --- a/crates/render/src/tests.rs +++ b/crates/render/src/tests.rs @@ -579,6 +579,57 @@ fn outline_view_draws_paths_from_kept_geometry() { } } +/// An Image Trace object listed in `trace_source` draws its source image on screen whatever its +/// View, also inside a group whose raster effect a renderer keeps; without the list, and in +/// exports, it draws its result. A group that is not an Image Trace object draws its members. +#[test] +fn trace_source_draws_the_source_image_of_the_listed_objects() { + use vectorcraft_doc::{ImageBlob, ImageObject}; + let img = image::RgbaImage::from_pixel(1, 1, image::Rgba([255, 0, 0, 255])); + let mut png = vec![]; + img.write_to(&mut std::io::Cursor::new(&mut png), image::ImageFormat::Png).unwrap(); + let mut d = Document::new(100.0, 100.0); + d.images.insert("red".into(), ImageBlob::new("image/png", png)); + // A group at `x`: the hidden red source image, then a blue traced square over it. + let group = |d: &mut Document, x: f64, traced: bool| { + let xf = Affine::translate((x, 10.0)) * Affine::scale(20.0); + let im = ImageObject { key: "red".into(), width: 1, height: 1, xf, link: None, placement: Default::default(), pixels: None }; + let mut source = Node::new(d.alloc_id(), NodeKind::Image(Box::new(im))); + source.visible = false; + let shape = rect_node(d, Rect::new(x, 10.0, x + 20.0, 30.0), Paint::solid(Color::rgb(0.0, 0.0, 1.0)), Paint::None, 0.0); + let mut g = Node::group(d.alloc_id(), vec![Arc::new(source), Arc::new(shape)]); + if traced { + g.extras_mut().trace = Some(Box::new(serde_json::json!({ "view": "tracingResult" }))); + } + g + }; + let plain = group(&mut d, 10.0, true); + let blurred = group(&mut d, 40.0, true); + let other = group(&mut d, 70.0, false); + let ids = Arc::new(vec![plain.id, blurred.id, other.id]); + let mut holder = Node::group(d.alloc_id(), vec![Arc::new(blurred)]); + holder.appearance.effects.push(vectorcraft_doc::Effect { id: "blur.gaussian".into(), params: serde_json::json!({ "radius": 1 }), visible: true }); + let l = d.layers[0].id; + for n in [plain, holder, other] { + d.insert(Some(l), usize::MAX, n).unwrap(); + } + let screen = RenderOptions { trace_views: true, background: Some([255; 4]), ..Default::default() }; + let held = RenderOptions { trace_source: Some(ids.clone()), ..screen.clone() }; + let export = RenderOptions { trace_source: Some(ids), background: Some([255; 4]), ..Default::default() }; + let red = |p: [u8; 4]| p[0] > 200 && p[2] < 60; + let blue = |p: [u8; 4]| p[2] > 200 && p[0] < 60; + let mut kept = Renderer::new(); + for (opts, source) in [(&screen, false), (&held, true), (&screen, false), (&export, false)] { + for r in [kept.render(&d, 100, 100, Affine::IDENTITY, opts), Renderer::new().render(&d, 100, 100, Affine::IDENTITY, opts)] { + for (x, what) in [(20, "the Image Trace object"), (50, "the one in the blurred group")] { + let p = r.pixel(x, 20); + assert!(if source { red(p) } else { blue(p) }, "{what} with trace_source {source}: {p:?}"); + } + assert!(blue(r.pixel(80, 20)), "the plain group draws its square: {:?}", r.pixel(80, 20)); + } + } +} + #[test] fn artboards_white_on_pasteboard() { let d = Document::new(50.0, 50.0); diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 305d09aa8..caf469ba8 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -419,6 +419,8 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { rich_black: app.session.prefs.black_on_screen == "rich", // A tool's drag on the canvas, during an interaction (not a dialog's preview). dragging: app.session.in_interaction() && ui.data(|d| d.get_temp::(drag_id())) == Some(Drag::Tool), + // The Image Trace panel's eye button, only while a pointer button is down. + trace_source: app.ui.trace_hold_source.clone().filter(|_| ui.input(|i| i.pointer.any_down())), }; // Placed documents' bitmaps are being made: draw again when they are ready. if vectorcraft_render::placed_document::busy() { @@ -487,6 +489,8 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { anti_alias: if app.session.prefs.anti_aliased_artwork { vectorcraft_render::AntiAlias::Art } else { vectorcraft_render::AntiAlias::None }, progressive_placed: true, trace_views: true, + // Image Trace › the eye button held: the source image of the selected objects. + trace_source: key.trace_source.clone(), smooth_images, // Isolation mode: the art around the isolated group or layer is dimmed (#833). isolated: key.isolated.map(vectorcraft_doc::NodeId), diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 610d7b398..76d8f99fa 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -3013,6 +3013,7 @@ font-technology Variable Променливи Outlines Контури Outlines with Source Image Контури с изходно изображение Source Image Изходно изображение + Press and hold to view the source image Задръжте натиснато, за да видите изходното изображение Image Trace View Изглед на трасиране на изображение Snap Options Опции за прилепване Snap to Glyph Прилепване към глиф diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 1f2320313..103492f50 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -3932,6 +3932,7 @@ font-technology Variable Variabilní View: Zobrazení: Tracing Result Výsledek vektorizace Outlines with Source Image Obrysy se zdrojovým obrazem + Press and hold to view the source image Podržením zobrazíte zdrojový obraz SVG Options Možnosti SVG Maximize Maximalizovat Restore Obnovit diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 29f633771..eb7905143 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -3675,6 +3675,7 @@ font-technology Variable Variable View: Visning: Tracing Result Sporingsresultat Outlines with Source Image Konturer med kildebillede + Press and hold to view the source image Hold nede for at se kildebilledet SVG Options SVG-indstillinger Maximize Maksimér Restore Gendan diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 6262eb08d..3af08e9c0 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -3051,6 +3051,7 @@ effect Points Punkte Outlines Pfade Outlines with Source Image Pfade mit Quellbild Source Image Quellbild + Press and hold to view the source image Gedrückt halten, um das Quellbild anzuzeigen Image Trace View Bildnachzeichner-Ansicht Snap Options Einrastoptionen Snap to Glyph An Glyphe ausrichten diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index b8d50b100..c0d6d1806 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -1881,6 +1881,7 @@ font-technology Variable Μεταβλητές Absolute Απόλυτο View: Προβολή: Outlines Περιγράμματα + Press and hold to view the source image Πατήστε παρατεταμένα για να δείτε την αρχική εικόνα Snap to Grid Προσκόλληση στο πλέγμα Snap to Pixel Προσκόλληση σε εικονοστοιχείο Snap to Point Προσκόλληση σε σημείο diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 26e1e0712..22755e74b 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -3081,6 +3081,7 @@ font-technology Variable Variables Outlines Contornos Outlines with Source Image Contornos con imagen de origen Source Image Imagen de origen + Press and hold to view the source image Mantén pulsado para ver la imagen de origen Image Trace View Vista del calco de imagen Snap Options Opciones de ajuste Snap to Glyph Ajustar a glifo diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index 004f41bc0..81cf5506f 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -3856,6 +3856,7 @@ font-technology Variable Muuttuvat View: Näkymä: Tracing Result Jäljityksen tulos Outlines with Source Image Ääriviivat ja lähdekuva + Press and hold to view the source image Pidä painettuna nähdäksesi lähdekuvan SVG Options SVG-asetukset Maximize Suurenna Restore Palauta diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 54c2b9102..b1c979f49 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -3078,6 +3078,7 @@ font-technology Variable Variables Outlines Contours Outlines with Source Image Tracés avec l’image source Source Image Image source + Press and hold to view the source image Maintenez le bouton enfoncé pour afficher l’image source Image Trace View Affichage de la vectorisation de l’image Snap Options Options de magnétisme Snap to Glyph Magnétisme du glyphe diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 38ad9d2ab..53d23106e 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -2039,6 +2039,7 @@ file-filter Templates Sablonok The file writes keys and modifiers as codes. Check the proposed meanings and answer the codes that are not known yet. A fájl a billentyűket és a módosítóbillentyűket kódként tárolja. Ellenőrizd a javasolt jelentéseket, és add meg a még ismeretlen kódok jelentését. Click, then press the key this code stands for (Esc cancels) Kattints, majd nyomd le azt a billentyűt, amelyet ez a kód jelöl (Esc: mégse) Press a key… Nyomj le egy billentyűt… + Press and hold to view the source image Tartsd lenyomva a forráskép megtekintéséhez from your earlier imports a korábbi importálásaidból from the format's public description a formátum nyilvános leírásából not known yet még ismeretlen diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index fb5a20a4d..a70582c8f 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -3675,6 +3675,7 @@ font-technology Variable Variabel View: Tampilan: Tracing Result Hasil penelusuran Outlines with Source Image Kontur dengan gambar sumber + Press and hold to view the source image Tekan dan tahan untuk melihat gambar sumber SVG Options Opsi SVG Maximize Maksimalkan Restore Pulihkan diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index b80f2b0bd..d20a4121f 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -3115,6 +3115,7 @@ mark-style Japanese Giapponese Outlines Contorni Outlines with Source Image Contorni con immagine di origine Source Image Immagine di origine + Press and hold to view the source image Tieni premuto per visualizzare l'immagine di origine Image Trace View Vista del ricalco immagine Snap Options Opzioni di aggancio Snap to Glyph Aggancia a glifo diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 4458d7a63..58910c75f 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -2889,6 +2889,7 @@ mark-style Japanese 日本式 Outlines アウトライン Outlines with Source Image アウトライン(元画像付き) Source Image 元画像 + Press and hold to view the source image 押している間、元画像を表示 Image Trace View 画像トレースの表示 Snap Options スナップオプション Snap to Unit 単位にスナップ diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 86b587900..e4f256d06 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -3456,6 +3456,7 @@ font-technology Variable ცვლადი View: ხედი: Tracing Result ვექტორიზაციის შედეგი Outlines with Source Image კონტურები საწყისი გამოსახულებით + Press and hold to view the source image ხანგრძლივად დააჭირეთ საწყისი გამოსახულების სანახავად SVG Options SVG-ის პარამეტრები Maximize გაშლა Restore აღდგენა diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 8a0f2e3fd..34b4e8053 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -2883,6 +2883,7 @@ mark-style Japanese 일본식 Outlines 윤곽선 Outlines with Source Image 원본 이미지 포함 윤곽선 Source Image 원본 이미지 + Press and hold to view the source image 누르고 있는 동안 원본 이미지 보기 Image Trace View 이미지 추적 보기 Snap Options 스냅 옵션 Snap to Unit 단위에 스냅 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 81ac2af5a..a05db9f2d 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -3689,6 +3689,7 @@ font-technology Variable Variabel View: Weergave: Tracing Result Traceringresultaat Outlines with Source Image Contouren met bronafbeelding + Press and hold to view the source image Ingedrukt houden om de bronafbeelding te bekijken SVG Options SVG-opties Maximize Maximaliseren Restore Herstellen diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 593c4fc04..56f68ce86 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -3771,6 +3771,7 @@ font-technology Variable Zmienne View: Widok: Tracing Result Wynik obrysu Outlines with Source Image Kontury z obrazem źródłowym + Press and hold to view the source image Przytrzymaj, aby wyświetlić obraz źródłowy SVG Options Opcje SVG Maximize Maksymalizuj Restore Przywróć diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 68666a175..85c269f62 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -3294,6 +3294,7 @@ font-technology Variable Variáveis Outlines Contornos Outlines with Source Image Contornos com imagem de origem Source Image Imagem de origem + Press and hold to view the source image Mantenha pressionado para ver a imagem de origem Image Trace View Exibição de vetorização de imagem Stroke (X), double-click for the Color Picker Traço (X), clique duas vezes para o seletor de cores Swap Fill and Stroke (Shift+X) Trocar preenchimento e traço (Shift+X) diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index e97d50df9..32b341dbd 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -3111,6 +3111,7 @@ mark-style Japanese Японский Outlines Контуры Outlines with Source Image Контуры с исходным изображением Source Image Исходное изображение + Press and hold to view the source image Удерживайте, чтобы увидеть исходное изображение Image Trace View Просмотр трассировки изображения Snap Options Параметры привязки Snap to Glyph Привязать к глифу diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 5af11984a..ff43b766f 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -3013,6 +3013,7 @@ font-technology Variable Variabla Outlines Konturlinjer Outlines with Source Image Konturlinjer med källbild Source Image Källbild + Press and hold to view the source image Håll ned för att visa källbilden Image Trace View Vy för bildkalkering Snap Options Fästalternativ Snap to Glyph Fäst mot glyf diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 2b39485c9..eb63ed592 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -2921,6 +2921,7 @@ font-technology Variable Değişken Outlines Ana hatlar Outlines with Source Image Kaynak Resimli Ana Hatlar Source Image Kaynak Resim + Press and hold to view the source image Kaynak resmi görmek için basılı tutun Image Trace View Görüntü İzleme Görünümü Snap Options Yapış Seçenekleri Snap to Glyph Glif'e Yapış diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index f889d0984..d49c9f499 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -3108,6 +3108,7 @@ effect Points Точки Outlines Контури Outlines with Source Image Контури з вихідним зображенням Source Image Вихідне зображення + Press and hold to view the source image Утримуйте, щоб побачити вихідне зображення Image Trace View Перегляд трасування зображення Snap Options Параметри прив’язки Snap to Glyph Прив’язати до гліфу diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 9b6c1e8ba..437422e55 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -1779,6 +1779,7 @@ file-filter Templates Mẫu The file writes keys and modifiers as codes. Check the proposed meanings and answer the codes that are not known yet. Tệp ghi phím và phím bổ trợ dưới dạng mã. Hãy kiểm tra các nghĩa được đề xuất và trả lời những mã chưa biết. Click, then press the key this code stands for (Esc cancels) Bấm, rồi nhấn phím mà mã này biểu thị (Esc để hủy) Press a key… Nhấn một phím… + Press and hold to view the source image Nhấn giữ để xem ảnh nguồn from your earlier imports từ các lần nhập trước của bạn from the format's public description từ mô tả công khai của định dạng not known yet chưa biết diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index e19751cd4..d097be59f 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -2947,6 +2947,7 @@ font-script Japanese 汉字 Outlines 轮廓 Outlines with Source Image 轮廓(带源图像) Source Image 源图像 + Press and hold to view the source image 按住以查看源图像 Image Trace View 图像描摹视图 Snap Options 对齐选项 Snap to Glyph 靠齐字符 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 4a9057aa5..5ad9dffb0 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -3878,6 +3878,7 @@ font-script Japanese 漢字 Outlines 外框 Outlines with Source Image 外框(含來源影像) Source Image 來源影像 + Press and hold to view the source image 按住以檢視來源影像 Image Trace View 影像描圖檢視 Snap Options 靠齊選項 Snap to Glyph 靠齊字符 diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index c236a0835..8136f57bd 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -430,6 +430,9 @@ pub struct CacheKey { pub rich_black: bool, /// Colour and style of guide paths ([`vectorcraft_render::RenderOptions::guide`]). pub guide: ([u8; 3], bool), + /// The Image Trace objects drawn with their source image + /// ([`vectorcraft_render::RenderOptions::trace_source`]). + pub trace_source: Option>>, } #[derive(Clone, Copy, Debug, Default)] diff --git a/crates/ui-egui/src/panels/image_trace.rs b/crates/ui-egui/src/panels/image_trace.rs index fac04c4ae..3f723116f 100644 --- a/crates/ui-egui/src/panels/image_trace.rs +++ b/crates/ui-egui/src/panels/image_trace.rs @@ -135,6 +135,25 @@ fn set_view(app: &mut VectorcraftApp, view: TraceView) { } } +/// While `down`, the selected Image Trace objects show their source image on the canvas, whatever +/// their View. The document and the undo history stay as they are. The panel's eye button next to +/// View calls it; per the public documentation of the panel, that control is pressed and held to +/// view the source image +/// (). +fn hold_source(app: &mut VectorcraftApp, ctx: &egui::Context, down: bool) { + let ids = app + .session + .active() + .filter(|_| down) + .map(|st| st.selection.objects.iter().copied().filter(|id| st.doc.node(*id).is_some_and(is_trace)).collect::>()) + .filter(|ids| !ids.is_empty()) + .map(std::sync::Arc::new); + if ids != app.ui.trace_hold_source { + app.ui.trace_hold_source = ids; + ctx.request_repaint(); + } +} + /// The selected Image Trace object's View row (Control bar, Properties). pub(crate) fn view_row(app: &mut VectorcraftApp, ui: &mut Ui, current: TraceView, width: f32) { widgets::dim_label(ui, tl!("View:")); @@ -295,12 +314,19 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { } ui.horizontal(|ui| { widgets::dim_label(ui, tl!("View:")); - if let Some(v) = view_dropdown(ui, st.view, 170.0) { + let eye = 24.0; + if let Some(v) = view_dropdown(ui, st.view, (ui.available_width() - eye - ui.spacing().item_spacing.x).min(170.0)) { st.view = v; if is_trace { set_view(app, v); } } + let held = app.ui.trace_hold_source.is_some(); + let r = widgets::icon_button_enabled(ui, "eye", tl!("Press and hold to view the source image"), held, is_trace, eye); + if cfg!(test) { + ui.ctx().data_mut(|d| d.insert_temp(egui::Id::new("trace-hold-source"), r.rect)); + } + hold_source(app, ui.ctx(), is_trace && r.is_pointer_button_down_on()); }); ui.horizontal(|ui| { widgets::dim_label(ui, tl!("Mode:")); @@ -837,6 +863,39 @@ mod tests { } } + /// Holding the eye button next to View down lists the selected Image Trace object for the + /// canvas to draw with its source image; releasing it shows the object's View again. The + /// document, its View and the undo history stay as they were. With a plain image selected the + /// button is disabled. + #[test] + fn holding_the_eye_button_shows_the_source_image_until_released() { + let mut app = traced_app(); + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + frame(&mut app, &ctx, vec![]); + let id = first_selected(&app).unwrap().id; + let undo = |app: &VectorcraftApp| app.session.active().unwrap().history.undo.len(); + let revision = |app: &VectorcraftApp| app.session.active().unwrap().revision; + let (steps, rev) = (undo(&app), revision(&app)); + let at = ctx.data(|d| d.get_temp::(egui::Id::new("trace-hold-source"))).unwrap().center(); + let press = |pressed| Event::PointerButton { pos: at, button: PointerButton::Primary, pressed, modifiers: Default::default() }; + frame(&mut app, &ctx, vec![Event::PointerMoved(at), press(true)]); + assert_eq!(app.ui.trace_hold_source.as_deref(), Some(&vec![id]), "held: the object shows its source image"); + frame(&mut app, &ctx, vec![]); + assert!(app.ui.trace_hold_source.is_some(), "still held on the next frame"); + frame(&mut app, &ctx, vec![press(false)]); + assert_eq!(app.ui.trace_hold_source, None, "released: the object shows its View again"); + assert_eq!(undo(&app), steps, "no undo step"); + assert_eq!(revision(&app), rev, "the document is unchanged"); + assert_eq!(selected_trace(&app).map(|t| t.1), Some(TraceView::Result), "its View is unchanged"); + app.run("imageTrace.release", json!({})).unwrap(); + assert!(matches!(first_selected(&app).unwrap().kind, NodeKind::Image(_))); + frame(&mut app, &ctx, vec![]); + frame(&mut app, &ctx, vec![Event::PointerMoved(at), press(true)]); + assert_eq!(app.ui.trace_hold_source, None, "a plain image has no source image to show"); + frame(&mut app, &ctx, vec![press(false)]); + } + #[test] fn properties_can_reopen_the_trace_controls() { let mut app = traced_app(); diff --git a/crates/ui-egui/src/state.rs b/crates/ui-egui/src/state.rs index dc76c2e2e..e3de3b0e2 100644 --- a/crates/ui-egui/src/state.rs +++ b/crates/ui-egui/src/state.rs @@ -549,6 +549,10 @@ pub struct UiState { /// becoming active, leaves it. #[serde(skip)] pub home: Option<(Option, usize)>, + /// The Image Trace objects the canvas draws with the Source Image view while the Image Trace + /// panel's eye button is held down ([`vectorcraft_render::RenderOptions::trace_source`]). + #[serde(skip)] + pub trace_hold_source: Option>>, /// Layers panel › Panel Options… (row size, thumbnails, Show Layers Only, Locate Selected Objects). #[serde(default)] pub layers_panel: crate::panels::layers::PanelOptions, @@ -902,6 +906,7 @@ impl Default for UiState { move_options: Value::Null, dxf_import: Value::Null, home: None, + trace_hold_source: None, layers_panel: Default::default(), layers_filter: Default::default(), image_trace_advanced: true, diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index d0fb0481c..d9e8728eb 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -788,6 +788,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - **Image Trace, thin closed outlines:** generated `fit::snap_area_tests` verify nonzero interiors after straightening, opposite closed-boundary winding and Noise controls; see [quality notes](image-trace-quality.md#thin-closed-outlines-with-snap-curves-to-lines). Scores and hours unchanged; open-piece fitting is unchanged. - **Image Trace, flattened slivers:** in a Color, Grayscale or Black and White trace with Fills, a traced region whose outline straightens onto one line (a single pixel, or a one-pixel row along an edge, its pieces shared with the regions beside it) is left out instead of becoming a selectable path with no area, which paints nothing. Snap Curves To Lines straightens more outlines, so it leaves out more of these. Without Fills, the region's 1 px stroke draws it, and it is kept. Code-drawn tests in `crates/trace`. - **Image Trace, Auto Color rare colours:** the Automatic palette keeps colours too rare for its area count when they are distinct (Oklab) and drawn as coherent regions, at most six, protected from mixed-colour merging and Noise; code-drawn stem, highlight, low-contrast and scattered-noise tests (see [quality notes](image-trace-quality.md#auto-color-keeps-rare-colours)). +- **Image Trace, source image on hold:** while the eye button next to the Image Trace panel's View is held down, the canvas shows the selected Image Trace objects' source image whatever their View, and shows their View again on release, with no change to the document or the undo history. - **Image Trace, large images:** images over 1500 px on a side trace with a warning in the result and the status bar; the anchor limit is 8,000,000 (2,000,000 on the web), measured at about 90 bytes an anchor (see [quality notes](image-trace-quality.md#large-images)). - **Image Trace, Preview:** the Image Trace panel's Preview checkbox, before Trace, is on by default, and a setting change traces the selected Image Trace object again at once. Off, changes wait in the panel until Trace applies them as one undo step, or until Preview is turned on again. Preview is kept with the UI preferences, and Reset to Default leaves it. - **Image Trace small contours:** closed fits too small to form an editable subpath retain their positioned outline, including holes. Source-generated island, hole/winding and stepped-region regressions cover Corners and Noise; defaults, parity scores and estimates are unchanged. diff --git a/docs/ui-parity.md b/docs/ui-parity.md index 4d658b8b3..ab7fa339b 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -274,6 +274,7 @@ empty), without opening the dialog. | Panels outside the app window (another monitor) | missing | single OS window | | Edit Toolbar (•••): the All Tools drawer opens beside the toolbar by its button (Escape or a click outside closes it), dims the tools already in the toolbar; while it is open, tools drag from it onto the toolbar (between two tools, or onto one to join its group), from the toolbar into it, and within the toolbar; each toolbar keeps its edits | done | #1018; `window.editToolbar`, `toolbar.addTool`/`removeTool`/`reset`; the drawer's Reset (Illustrator has it in a drawer menu); dragging a toolbar button moves the tool it shows, not its whole group; New/Manage Toolbars missing; tool flyouts are as wide as their longest translated name and shortcut need (#1616) | | Quick Image Trace presets trace at once and leave the panel as it was; the Image Trace button (Control bar, Properties) opens the editable controls | done | Per the public documentation, choosing a preset traces the image with it straight away, and the panel is opened with the Image Trace button in the Control panel or in Properties' Image Trace section (). The controls show Threshold, Paths, Corners and Noise with Advanced open; `panels/image_trace.rs` headless tests (`a_quick_trace_leaves_the_panel_as_it_was`, `sketch_quick_trace_reveals_all_adjustments_and_keeps_the_source` for the button's route). No algorithm parity claim. | +| Image Trace panel: holding the eye button next to View down shows the selected Image Trace objects' source image on the canvas | done | Per the public documentation, the control next to View is pressed and held to compare the trace result with the source image (). On release the objects show their View again; the document, its View and the undo history are unchanged. `RenderOptions::trace_source`; `panels/image_trace.rs` headless test `holding_the_eye_button_shows_the_source_image_until_released`, render test `trace_source_draws_the_source_image_of_the_listed_objects`. The Control bar and Properties have no such button | | Live trace result bounds and selection highlights exclude the retained bitmap; source-image views retain it | done | All TraceView modes and rotated result bounds regression-covered; ordinary hidden group behavior retained | | Image Trace panel: the Preview checkbox before Trace chooses whether a setting change traces the selected Image Trace object again at once | done | Per the public documentation, Preview shows the result of the current settings and Trace traces with them (). Preview is on by default and kept with the UI preferences; Reset to Default leaves it. Off, changes wait in the panel until Trace applies them as one undo step, or until Preview is turned on again. `ui.set {imageTracePreview}`; `panels/image_trace.rs` headless tests (`with_preview_off_a_slider_change_waits_for_trace`, `turning_preview_on_traces_the_changes_that_waited`, `preview_turned_on_with_nothing_selected_traces_when_the_object_is_selected_again`, `settings_that_wait_stay_with_their_object`, `preview_is_kept_with_the_preferences_and_reset_to_default_leaves_it`) | | Toolbar without group labels by default, one or two columns; All Tools drawer by flyout group with shortcuts, its menu (Reset, Basic, Advanced) and Show section | done | #1751; a preference brings the labels back; `toolbar.show` sets the Fill/Stroke, Coloring, Draw Mode and Screen Mode controls |