Merge pull request #3313 from dotCooCoo/feat/view-show-live-paint-gaps

View: Show Live Paint Gaps highlights the gaps of the selected Live Paint groups
This commit is contained in:
BFlat
2026-10-21 16:16:29 +07:00
committed by GitHub
33 changed files with 192 additions and 5 deletions
+6
View File
@@ -662,6 +662,12 @@ fn open_gaps(group: &Node, options: GapOptions) -> Vec<[Point; 2]> {
po::find_gaps(&source_shapes(&source_nodes(group)).0, options.size.points())
}
/// The gaps View › Show Live Paint Gaps shows on `group`: those up to its Gap Options' size wide,
/// as `livePaint.info` lists them. None when `group` is not a Live Paint group.
pub fn live_paint_gaps(group: &Node) -> Vec<[Point; 2]> {
if is_live_paint(group) { open_gaps(group, group.gap_options()) } else { vec![] }
}
fn lp_gap_options(s: &mut Session, p: &Value) -> Result<Value> {
const C: &str = "livePaint.gapOptions";
let detect = match p.get("detect").filter(|v| !v.is_null()) {
+3
View File
@@ -525,6 +525,9 @@ pub use flatten::FlattenerPreset;
/// The shapes a group selected alone gives the Pathfinder panel.
pub use pathops::shape_count;
/// The gaps View › Show Live Paint Gaps shows on a Live Paint group.
pub use buildcmds::live_paint_gaps;
/// Command ids kept only so older scripts keep working: `(alias, the command it duplicates)`.
/// They run as before but the command palette leaves them out (it lists the command once).
pub const ALIASES: &[(&str, &str)] = &[("object.convertDocumentColorMode", "file.documentColorMode")];
+84
View File
@@ -553,6 +553,9 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
if app.ui.view.text_threads {
thread_overlay(app, &painter, &xf);
}
if app.ui.view.live_paint_gaps {
overlays.extend(live_paint_gap_overlays(app));
}
if app.ui.view.hidden_chars {
app.canvas.hidden_chars = hidden_chars;
hidden_chars_overlay(app, &painter, &xf);
@@ -2246,6 +2249,22 @@ fn thread_overlay(app: &VectorcraftApp, p: &egui::Painter, xf: &Xf) {
}
}
/// View → Show Live Paint Gaps: the gaps of the selected Live Paint groups in each group's Gap
/// Preview Color ([`crate::dialogs::live_paint_gaps::selected_gaps`]). Cached per revision.
fn live_paint_gap_overlays(app: &mut VectorcraftApp) -> Vec<Overlay> {
let Some(st) = app.session.active() else { return vec![] };
let key = (st.uid, st.revision);
let gaps = match &app.canvas.live_paint_gaps {
Some((k, gaps)) if *k == key => gaps.clone(),
_ => {
let gaps = std::sync::Arc::new(crate::dialogs::live_paint_gaps::selected_gaps(app));
app.canvas.live_paint_gaps = Some((key, gaps.clone()));
gaps
}
};
gaps.to_vec()
}
/// Simplify's Show Original Path: the paths its preview ran on, as they were before it, in a band
/// 3 screen points wide of their layer's color at 40%, under the selection's outlines. Nothing is drawn
/// when they have more than [`OVERLAY_MAX_ANCHORS`] anchor points.
@@ -5840,6 +5859,71 @@ mod tests {
assert!(area(&app), "a double-click on the widget converts to area type");
}
/// View → Show Live Paint Gaps draws the gaps of each selected Live Paint group in the group's
/// Gap Preview Color, and nothing for a group or path that is not a selected Live Paint group;
/// Hide Live Paint Gaps and Hide Edges hide them.
#[test]
fn show_live_paint_gaps_draws_the_selected_groups_gaps_in_their_preview_color() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.session.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
// Open paths round a square, each ending 2 pt short of its start: one Small Gap each.
let square = |app: &mut VectorcraftApp, x: i64| {
let anchors = [(0, 0), (100, 0), (100, 100), (0, 100), (0, 2)].map(|(dx, y)| json!({"x": x + dx, "y": y}));
app.session.execute("path.create", &json!({ "anchors": anchors })).unwrap()["id"].as_u64().unwrap()
};
let mut groups = vec![];
for (x, color) in [(0, "#123456"), (150, "#654321")] {
let p = square(&mut app, x);
app.session.execute("select.set", &json!({"ids": [p]})).unwrap();
let g = app.session.execute("livePaint.make", &json!({})).unwrap()["id"].as_u64().unwrap();
app.session.execute("livePaint.gapOptions", &json!({"id": g, "color": color})).unwrap();
groups.push(g);
}
let plain = square(&mut app, 300);
let ctx = egui::Context::default();
let gaps = |app: &mut VectorcraftApp, rgb: [u8; 3]| {
let solid = egui::epaint::ColorMode::Solid(c32(rgb));
shapes(app, &ctx).iter().filter(|s| matches!(s, Shape::Path(ps) if ps.stroke.color == solid && ps.stroke.width == 2.0)).count()
};
let (first, second) = ([0x12, 0x34, 0x56], [0x65, 0x43, 0x21]);
// The color the plain path's gaps would be drawn in.
let plain_color = vectorcraft_doc::GapOptions::default().color;
app.session.execute("select.set", &json!({"ids": [groups[0], plain]})).unwrap();
assert_eq!((gaps(&mut app, first), gaps(&mut app, second)), (0, 0), "off until the item is chosen");
// View → Show Live Paint Gaps runs the command.
let tree = crate::menus::menu_tree();
let (_, view) = tree.iter().find(|(t, _)| *t == "View").unwrap();
assert!(view.iter().any(|i| matches!(i, crate::menus::Item::Cmd("Show Live Paint Gaps", "view.livePaintGaps", _))), "{view:?}");
crate::menus::invoke(&mut app, "view.livePaintGaps", json!({}));
assert!(app.ui.view.live_paint_gaps);
assert_eq!(
(gaps(&mut app, first), gaps(&mut app, second), gaps(&mut app, plain_color)),
(1, 0, 0),
"the selected group only; the plain path shows none"
);
app.session.execute("select.set", &json!({"ids": [groups[1]]})).unwrap();
assert_eq!((gaps(&mut app, first), gaps(&mut app, second)), (0, 1));
// The lines follow the group's Gap Preview Color and Paint Stops At size.
app.session.execute("livePaint.gapOptions", &json!({"id": groups[1], "color": "#123456"})).unwrap();
assert_eq!((gaps(&mut app, first), gaps(&mut app, second)), (1, 0));
app.session.execute("livePaint.gapOptions", &json!({"id": groups[1], "size": 1})).unwrap();
assert_eq!(gaps(&mut app, first), 0, "a 2 pt gap is wider than 1 pt");
app.session.execute("livePaint.gapOptions", &json!({"id": groups[1], "size": "small"})).unwrap();
app.ui.view.edges = false;
assert_eq!(gaps(&mut app, first), 0, "Hide Edges hides them");
app.ui.view.edges = true;
assert_eq!(gaps(&mut app, first), 1);
app.run("view.livePaintGaps", json!({})).unwrap();
assert!(!app.ui.view.live_paint_gaps);
assert_eq!(gaps(&mut app, first), 0, "View → Hide Live Paint Gaps");
// The setting is kept with the interface state; a state saved before it existed loads with it off.
app.ui.view.live_paint_gaps = true;
let mut saved = serde_json::to_value(&app.ui).unwrap();
assert_eq!(saved["view"]["live_paint_gaps"], json!(true));
saved["view"].as_object_mut().unwrap().remove("live_paint_gaps");
assert!(!serde_json::from_value::<crate::UiState>(saved).unwrap().view.live_paint_gaps);
}
/// #511: a star shows a widget in each of its ten corners with Direct Selection (none with
/// the Selection tool: it isn't a live shape); View → Hide Corner Widget hides them.
#[test]
+24 -5
View File
@@ -2,7 +2,8 @@
//! or Custom with its size), Gap Preview Color (a layer color or a custom color) and Close Gaps
//! With Paths for the selected Live Paint groups, opened on the first group's options. With Preview
//! on, the options apply to the groups live and the gaps they find draw on the canvas in the preview
//! color; OK keeps them as one undo step (`livePaint.gapOptions`).
//! color; OK keeps them as one undo step (`livePaint.gapOptions`). View › Show Live Paint Gaps
//! draws the gaps of the selected groups the same way ([`selected_gaps`]).
//!
//! Fields: `detect`, `size` (`small` | `medium` | `large` | `custom`), `custom` (pt), `color`
//! (`#rrggbb`, or a preset's name such as "Red"), `closeGaps`, `preview`, `__ids` (the groups) and
@@ -139,14 +140,32 @@ pub(crate) fn overlays(app: &VectorcraftApp) -> Vec<Overlay> {
.filter_map(|gap| {
let v: Vec<f64> = gap.as_array()?.iter().filter_map(Value::as_f64).collect();
let [x0, y0, x1, y1] = v.as_slice() else { return None };
let mut path = BezPath::new();
path.move_to(Point::new(*x0, *y0));
path.line_to(Point::new(*x1, *y1));
Some(Overlay::Path { path, color: [r, g, b], width: 2.0, dashed: false })
Some(gap_line(Point::new(*x0, *y0), Point::new(*x1, *y1), [r, g, b]))
})
.collect()
}
/// View › Show Live Paint Gaps: the gaps of the selected Live Paint groups, as lines in each
/// group's Gap Preview Color, drawn as the preview draws them.
pub(crate) fn selected_gaps(app: &VectorcraftApp) -> Vec<Overlay> {
let Some(st) = app.session.active() else { return vec![] };
let groups = st.selection.objects.iter().filter_map(|id| st.doc.node(*id)).filter(|n| vectorcraft_tools::builder::is_live_paint(n));
groups
.flat_map(|n| {
let color = n.gap_options().color;
vectorcraft_engine::cmd::live_paint_gaps(n).into_iter().map(move |[a, b]| gap_line(a, b, color))
})
.collect()
}
/// A gap from `a` to `b` as a line in `color`, 2 screen points wide.
fn gap_line(a: Point, b: Point, color: [u8; 3]) -> Overlay {
let mut path = BezPath::new();
path.move_to(a);
path.line_to(b);
Overlay::Path { path, color, width: 2.0, dashed: false }
}
#[cfg(test)]
mod tests {
use super::*;
+2
View File
@@ -1175,6 +1175,8 @@
Large Gaps Големи пролуки
Gap Preview Color: Цвят за преглед на пролуките:
Close Gaps With Paths Затваряне на пролуките с пътища
Show Live Paint Gaps Показване на пролуките на динамичното оцветяване
Hide Live Paint Gaps Скриване на пролуките на динамичното оцветяване
Select Fills Избиране на запълвания
Select Strokes Избиране на щрихове
Gaussian Blur… Гаусово размазване…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -265,6 +265,8 @@
Large Gaps Velké mezery
Gap Preview Color: Barva náhledu mezer:
Close Gaps With Paths Uzavřít mezery cestami
Show Live Paint Gaps Zobrazit mezery živé malby
Hide Live Paint Gaps Skrýt mezery živé malby
Select Fills Vybírat výplně
Select Strokes Vybírat tahy
Image Trace Vektorizace obrazu
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5093,6 +5093,8 @@ swatch Bright Violet Kraftig violet
Gap Detection Registrering af huller
Gap Options Indstillinger for huller
Gap Preview Color: Eksempelfarve for huller:
Show Live Paint Gaps Vis huller i live-maling
Hide Live Paint Gaps Skjul huller i live-maling
Graph Column Diagramsøjle
JavaScript Files JavaScript-filer
JavaScript: JavaScript:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1193,6 +1193,8 @@ file-filter Templates Vorlagen
Large Gaps Große Lücken
Gap Preview Color: Vorschaufarbe für Lücken:
Close Gaps With Paths Lücken mit Pfaden schließen
Show Live Paint Gaps Lücken beim interaktiven Malen einblenden
Hide Live Paint Gaps Lücken beim interaktiven Malen ausblenden
Select Fills Flächen auswählen
Select Strokes Konturen auswählen
Gaussian Blur… Gaußscher Weichzeichner…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1140,6 +1140,8 @@
Large Gaps Μεγάλα διάκενα
Gap Preview Color: Χρώμα προεπισκόπησης διακένων:
Close Gaps With Paths Κλείσιμο διακένων με διαδρομές
Show Live Paint Gaps Εμφάνιση διακένων δυναμικής βαφής
Hide Live Paint Gaps Απόκρυψη διακένων δυναμικής βαφής
Select Fills Επιλογή γεμισμάτων
Select Strokes Επιλογή περιγραμμάτων
Gaussian Blur… Θόλωση Γκάους…
1 # Ελληνικά (el) — VectorCraft
1140
1141
1142
1143
1144
1145
1146
1147
+2
View File
@@ -1200,6 +1200,8 @@
Large Gaps Huecos grandes
Gap Preview Color: Color de previsualización de huecos:
Close Gaps With Paths Cerrar huecos con trazados
Show Live Paint Gaps Mostrar huecos de pintura interactiva
Hide Live Paint Gaps Ocultar huecos de pintura interactiva
Select Fills Seleccionar rellenos
Select Strokes Seleccionar trazos
Gaussian Blur… Desenfoque gaussiano…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -272,6 +272,8 @@
Large Gaps Suuret aukot
Gap Preview Color: Aukkojen esikatseluväri:
Close Gaps With Paths Sulje aukot poluilla
Show Live Paint Gaps Näytä elävän maalauksen aukot
Hide Live Paint Gaps Piilota elävän maalauksen aukot
Select Fills Valitse täytöt
Select Strokes Valitse viivat
Image Trace Kuvan jäljitys
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1199,6 +1199,8 @@
Large Gaps Grands espaces
Gap Preview Color: Couleur d’aperçu des espaces :
Close Gaps With Paths Fermer les espaces avec des tracés
Show Live Paint Gaps Afficher les espaces de peinture dynamique
Hide Live Paint Gaps Masquer les espaces de peinture dynamique
Select Fills Sélectionner les fonds
Select Strokes Sélectionner les contours
Gaussian Blur… Flou gaussien…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1505,6 +1505,8 @@ file-filter Templates Sablonok
Large Gaps Nagy hézagok
Gap Preview Color: Hézagok előnézeti színe:
Close Gaps With Paths Hézagok lezárása útvonalakkal
Show Live Paint Gaps Élő festés hézagainak megjelenítése
Hide Live Paint Gaps Élő festés hézagainak elrejtése
Select Fills Kitöltések kijelölése
Select Strokes Körvonalak kijelölése
Gaussian Blur Gauss-elmosás
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5093,6 +5093,8 @@ swatch Bright Violet Violet cerah
Gap Detection Deteksi celah
Gap Options Opsi celah
Gap Preview Color: Warna pratinjau celah:
Show Live Paint Gaps Tampilkan celah Live Paint
Hide Live Paint Gaps Sembunyikan celah Live Paint
Graph Column Kolom grafik
JavaScript Files File JavaScript
JavaScript: JavaScript:
Can't render this file because it contains an unexpected character in line 3775 and column 19.
+2
View File
@@ -1209,6 +1209,8 @@ file-filter Templates Modelli
Large Gaps Spazi vuoti grandi
Gap Preview Color: Colore anteprima spazi vuoti:
Close Gaps With Paths Chiudi spazi vuoti con tracciati
Show Live Paint Gaps Mostra spazi vuoti pittura dinamica
Hide Live Paint Gaps Nascondi spazi vuoti pittura dinamica
Select Fills Seleziona riempimenti
Select Strokes Seleziona tracce
Gaussian Blur… Sfocatura gaussiana…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -263,6 +263,8 @@
Large Gaps 大きい間隔
Gap Preview Color: 間隔のプレビューカラー:
Close Gaps With Paths パスで間隔を閉じる
Show Live Paint Gaps ライブペイントの間隔を表示
Hide Live Paint Gaps ライブペイントの間隔を隠す
Select Fills 塗りを選択
Select Strokes 線を選択
Image Trace 画像トレース
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4980,6 +4980,8 @@ imageTrace Outline კონტური
Gap Detection ღრეჩოების აღმოჩენა
Gap Options ღრეჩოს პარამეტრები
Gap Preview Color: ღრეჩოს ნახვის ფერი:
Show Live Paint Gaps ცოცხალი შეღებვის ღრეჩოების ჩვენება
Hide Live Paint Gaps ცოცხალი შეღებვის ღრეჩოების დამალვა
Glow Brightness ნათების სიკაშკაშე
Glow Size ნათების ზომა
Graph Column დიაგრამის სვეტი
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -259,6 +259,8 @@
Large Gaps 큰 간격
Gap Preview Color: 간격 미리 보기 색상:
Close Gaps With Paths 패스로 간격 닫기
Show Live Paint Gaps 라이브 페인트 간격 표시
Hide Live Paint Gaps 라이브 페인트 간격 숨기기
Select Fills 칠 선택
Select Strokes 선 선택
Image Trace 이미지 추적
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5105,6 +5105,8 @@ swatch Bright Violet Felviolet
Gap Detection Openingen detecteren
Gap Options Openingsopties
Gap Preview Color: Voorbeeldkleur openingen:
Show Live Paint Gaps Live Paint-openingen tonen
Hide Live Paint Gaps Live Paint-openingen verbergen
Graph Column Grafiekkolom
JavaScript Files JavaScript-bestanden
JavaScript: JavaScript:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -230,6 +230,8 @@
Large Gaps Duże przerwy
Gap Preview Color: Kolor podglądu przerw:
Close Gaps With Paths Zamknij przerwy ścieżkami
Show Live Paint Gaps Pokaż przerwy aktywnego malowania
Hide Live Paint Gaps Ukryj przerwy aktywnego malowania
Select Fills Zaznaczaj wypełnienia
Select Strokes Zaznaczaj obrysy
Image Trace Obrys obrazu
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1057,6 +1057,8 @@
Large Gaps Lacunas Grandes
Gap Preview Color: Cor de Visualização das Lacunas:
Close Gaps With Paths Fechar Lacunas com Caminhos
Show Live Paint Gaps Mostrar Lacunas da Pintura ao Vivo
Hide Live Paint Gaps Ocultar Lacunas da Pintura ao Vivo
Select Fills Selecionar Preenchimentos
Select Strokes Selecionar Traçados
Gaussian Blur… Desfoque Gaussiano…
Can't render this file because it contains an unexpected character in line 6 and column 47.
+2
View File
@@ -1207,6 +1207,8 @@ file-filter Templates Шаблоны
Large Gaps Большие зазоры
Gap Preview Color: Цвет просмотра зазоров:
Close Gaps With Paths Закрыть зазоры контурами
Show Live Paint Gaps Показать зазоры быстрой заливки
Hide Live Paint Gaps Скрыть зазоры быстрой заливки
Select Fills Выделять заливки
Select Strokes Выделять обводки
Gaussian Blur… Размытие по Гауссу…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1177,6 +1177,8 @@
Large Gaps Stora mellanrum
Gap Preview Color: Förhandsgranskningsfärg för mellanrum:
Close Gaps With Paths Stäng mellanrum med banor
Show Live Paint Gaps Visa mellanrum i direktmålning
Hide Live Paint Gaps Dölj mellanrum i direktmålning
Select Fills Markera fyllningar
Select Strokes Markera konturer
Gaussian Blur… Gaussisk oskärpa…
Can't render this file because it contains an unexpected character in line 5 and column 25.
+1
View File
@@ -931,6 +931,7 @@ fn toggled_labels() -> Vec<String> {
"view.textThreads",
"type.hiddenCharacters",
"view.gradientAnnotator",
"view.livePaintGaps",
"view.artboards",
"view.template",
"view.rulers",
+2
View File
@@ -1203,6 +1203,8 @@ file-filter Templates Шаблони
Large Gaps Великі проміжки
Gap Preview Color: Колір перегляду проміжків:
Close Gaps With Paths Закрити проміжки контурами
Show Live Paint Gaps Показати проміжки швидкої заливки
Hide Live Paint Gaps Приховати проміжки швидкої заливки
Select Fills Виділяти заливки
Select Strokes Виділяти обведення
Gaussian Blur… Розмиття за Гауссом…
Can't render this file because it contains an unexpected character in line 725 and column 40.
+2
View File
@@ -1395,6 +1395,8 @@ file-filter Templates Mẫu
Large Gaps Khoảng cách lớn
Gap Preview Color: Màu xem trước khoảng cách:
Close Gaps With Paths Đóng khoảng cách bằng đường dẫn
Show Live Paint Gaps Hiện khoảng cách tô trực tiếp
Hide Live Paint Gaps Ẩn khoảng cách tô trực tiếp
Select Fills Chọn vùng tô
Select Strokes Chọn nét
Gaussian Blur Mờ Gaussian
1 # Vietnamese (vi) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
1395
1396
1397
1398
1399
1400
1401
1402
+2
View File
@@ -1156,6 +1156,8 @@ file-filter Templates 模板
Large Gaps 大间隙
Gap Preview Color: 间隙预览颜色:
Close Gaps With Paths 用路径封闭间隙
Show Live Paint Gaps 显示实时上色间隙
Hide Live Paint Gaps 隐藏实时上色间隙
Select Fills 选择填色
Select Strokes 选择描边
Gaussian Blur… 高斯模糊…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1180,6 +1180,8 @@ file-filter Templates 範本
Large Gaps 大間隙
Gap Preview Color: 間隙預覽顏色:
Close Gaps With Paths 以路徑封閉間隙
Show Live Paint Gaps 顯示即時上色間隙
Hide Live Paint Gaps 隱藏即時上色間隙
Select Fills 選取填色
Select Strokes 選取筆畫
Gaussian Blur… 高斯模糊…
Can't render this file because it contains an unexpected character in line 6 and column 47.
+7
View File
@@ -321,6 +321,8 @@ pub struct CanvasCache {
pub slices: Option<SliceCache>,
/// The print tiling's pages for (document uid, revision) (View → Show Print Tiling).
pub print_tiling: Option<PrintTilingCache>,
/// The gap lines View → Show Live Paint Gaps draws for (document uid, revision).
pub live_paint_gaps: Option<LivePaintGapsCache>,
/// The marks Type → Show Hidden Characters drew on each text object in the last frame. A frame
/// that does not draw them drops them ([`canvas::show`]).
pub hidden_chars: canvas::HiddenChars,
@@ -337,6 +339,10 @@ pub struct CanvasCache {
/// [`CanvasCache::slices`]: the layout of the slices of (document uid, revision).
pub type SliceCache = ((u64, u64), std::sync::Arc<Vec<vectorcraft_doc::SliceArea>>);
/// [`CanvasCache::live_paint_gaps`]: the gap lines of the selected Live Paint groups of (document
/// uid, revision).
pub type LivePaintGapsCache = ((u64, u64), std::sync::Arc<Vec<vectorcraft_tools::Overlay>>);
/// [`CanvasCache::print_tiling`]: the print tiling of (document uid, revision).
pub type PrintTilingCache = ((u64, u64), std::sync::Arc<Vec<vectorcraft_pdf::TilingPage>>);
@@ -539,6 +545,7 @@ impl VectorcraftApp {
worker_started: false,
slices: None,
print_tiling: None,
live_paint_gaps: None,
hidden_chars: Default::default(),
selection_box: None,
selection_bounds: None,
+12
View File
@@ -207,6 +207,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
),
("view.textThreads", "Hide Text Threads", "Cmd+Shift+Y", "{} toggle the thread lines between threaded text frames"),
("view.gradientAnnotator", "Hide Gradient Annotator", "Cmd+Alt+G", "{} toggle the Gradient tool's annotator"),
(
"view.livePaintGaps",
"Show Live Paint Gaps",
"",
"{} toggle drawing the gaps of the selected Live Paint groups (those livePaint.info lists) in each group's Gap Preview Color while edges show → true when on",
),
("type.hiddenCharacters", "Show Hidden Characters", "Cmd+Alt+I", "{} toggle markers for spaces, paragraph ends and story ends"),
("type.alignLeft", "Align left", "Cmd+Shift+L", "{} align the selected type's paragraphs (or those the Type tool's selection touches) left"),
(
@@ -1231,6 +1237,7 @@ pub fn run_ui_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<R
crate::panels::paragraph::align(app, justify)
}
"view.gradientAnnotator" => flag(&mut app.ui.view.gradient_annotator),
"view.livePaintGaps" => flag(&mut app.ui.view.live_paint_gaps),
"effect.last" => match app.last_effect.clone() {
Some((e, params)) => {
let label = vectorcraft_effects::effect_info(&e).map(|i| i.label.trim_end_matches('…').to_string()).unwrap_or_else(|| e.clone());
@@ -1872,6 +1879,7 @@ pub fn dynamic_label(app: &VectorcraftApp, id: &str, label: &str) -> String {
"view.textThreads" => if v.text_threads { "Hide Text Threads" } else { "Show Text Threads" }.into(),
"type.hiddenCharacters" => if v.hidden_chars { "Hide Hidden Characters" } else { "Show Hidden Characters" }.into(),
"view.gradientAnnotator" => if v.gradient_annotator { "Hide Gradient Annotator" } else { "Show Gradient Annotator" }.into(),
"view.livePaintGaps" => if v.live_paint_gaps { "Hide Live Paint Gaps" } else { "Show Live Paint Gaps" }.into(),
"effect.last" => match &app.last_effect {
Some((e, _)) => format!("Last Effect: {}", vectorcraft_effects::effect_info(e).map(|i| i.label).unwrap_or(e.as_str())),
None => label.into(),
@@ -2900,6 +2908,7 @@ pub fn menu_tree_named(english_names: bool) -> Vec<(&'static str, Vec<Item>)> {
c("Hide Text Threads", "view.textThreads"),
c("Hide Gradient Annotator", "view.gradientAnnotator"),
c("Hide Corner Widget", "view.cornerWidget"),
c("Show Live Paint Gaps", "view.livePaintGaps"),
Sep,
sub(
"Guides",
@@ -3970,6 +3979,8 @@ pub const DYNAMIC_LABELS: &[&str] = &[
"Show Hidden Characters",
"Hide Gradient Annotator",
"Show Gradient Annotator",
"Hide Live Paint Gaps",
"Show Live Paint Gaps",
"Hide Artboards",
"Show Artboards",
"Hide Rulers",
@@ -4243,6 +4254,7 @@ mod tests {
for (id, on, off) in [
("view.textThreads", "Hide Text Threads", "Show Text Threads"),
("view.gradientAnnotator", "Hide Gradient Annotator", "Show Gradient Annotator"),
("view.livePaintGaps", "Show Live Paint Gaps", "Hide Live Paint Gaps"),
("type.hiddenCharacters", "Show Hidden Characters", "Hide Hidden Characters"),
("view.template", "Hide Template", "Show Template"),
] {
+5
View File
@@ -231,6 +231,10 @@ pub struct ViewFlags {
pub text_threads: bool,
/// View → Show/Hide Gradient Annotator.
pub gradient_annotator: bool,
/// View → Show/Hide Live Paint Gaps: draw the gaps of the selected Live Paint groups in their Gap
/// Preview Color.
#[serde(default)]
pub live_paint_gaps: bool,
/// Type → Show Hidden Characters.
pub hidden_chars: bool,
/// View → Hide/Show Template: template layers drawn (dimmed) or left out of the canvas.
@@ -258,6 +262,7 @@ impl Default for ViewFlags {
snap_to_glyph: false,
text_threads: true,
gradient_annotator: true,
live_paint_gaps: false,
hidden_chars: false,
templates: true,
}
+3
View File
@@ -1270,6 +1270,9 @@ already walks Image Trace. Short agent entry points:
and `livePaint.info` list the gaps up to `size` wide that no path closes, as `[x0, y0, x1, y1]`. `livePaint.merge`
keeps the bottom group's options when its Gap Detection is on, and otherwise turns Gap Detection on with `small`
when another merged group has it on. When Gap Detection is off in every merged group, it stays off.
`view.livePaintGaps` (View › Show Live Paint Gaps, or Hide Live Paint Gaps while it is on) turns the drawing of
those gaps on or off. While it is on and edges show, the canvas draws the gaps of each selected Live Paint group in
that group's Gap Preview Color. The reply is the new state (`true` when on).
The Live Paint Selection tool's options (`tool.setOption {tool: "livePaintSelection", values}`; double-clicking the
tool opens them, dialog `livePaintSelectionOptions`) are `selectFills` and `selectStrokes` (both on), `color` (the
highlight color, `#rrggbb` or a layer color's name; `#ff2a2a`) and `width` (the highlight's width, 1 to 20 screen
+1
View File
@@ -669,6 +669,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Advanced art:** live Blends (steps/distance/smooth colour, editable spine, anchor-targeted Blend tool, faithful interpolation, knockout), Envelope Distort (warp/mesh/top object, Reset, full Envelope Options, mesh handles, Edit Contents; type, images, symbols, gradients, patterns and appearance distorted everywhere), Liquify tools with options and pen pressure, Puppet Warp with rotating pins, Perspective Grid with presets, movable planes and projected type (choosing a perspective tool shows the grid; Hide Grid hides it with the tool still chosen), Gradient Mesh, Shape Builder (stroke-only open paths and lines cut the regions they cross and enclose regions of their own, and regions they bound merge without seams where the lines cross at rounded coordinates (#893); filled open paths count as closed; erase mode while Alt/Option is held: a minus pointer, red highlights of what will go, a click or drag deletes the regions touched, the pieces of open lines and the edges (outline pieces between the points where paths meet, opening their paths there) it passes over, one undo step, `shapeBuilder.merge {erase}` and `shapeBuilder.regions` edges over MCP), Live Paint (every path an edge, open lines included), Image Trace (12 presets, colour layers traced in parallel; a trace past two million anchor points is refused with advice rather than running out of memory, an image over 64 megapixels is traced scaled down to that size (aspect kept, paths at the image's own size) and one over 192 megapixels is refused, and a selection too large to outline shows its bounds (#525); Object › Image Trace › Make and Make and Expand trace at once with the Default preset, #543; with an image selected the Control bar and the Properties panel show Image Trace, whose arrow traces with any preset, Mask (`object.maskImage`: a clipping path on the image's outline, selected so its handles crop), Crop Image (a crop box on the image, starting on its part over the artboard, dragged by its handles or inside it, the outside dimmed, with X/Y/W/H, Apply and Cancel in the Control bar, Enter and Escape, #734; `object.cropImage {rect}` for agents, `{trim: true}` cuts an image to its pixels that aren't fully transparent, pixel for pixel) and Embed or Unembed in place of Fill and Stroke; with an Image Trace object selected, its Preset (choosing another traces again), View, the Image Trace panel and Expand, plus Release in Properties; the five View modes (Tracing Result, with Outlines, Outlines, Outlines with Source Image, Source Image) redraw a trace on screen without tracing again, are saved with it and `imageTrace.setView` sets them, while exports and Expand keep the result; the panel's Advanced section is a header that stays open or closed as it was left (part of #641); Color mode's Palette: Limited, Full Tone (as many colours as the tones need, Less … More), Automatic (flat art with its own colours, else Full Tone; a heuristic) and Document Library (the most used colours of the document's swatches or a swatch library, filled with the swatch colours as they are), part of #641; Advanced › Create: Fills and/or Strokes, where lines up to the Stroke width trace as stroked centre lines (distance transform, thinning, a skeleton graph with spurs pruned, curve fitting; one width per line; without Fills, wider areas become stroked outlines), part of #641), pattern swatches with pattern editing mode, live Repeat (radial/grid/mirror). A single selected mirror repeat shows a prominent axis with a center move handle and two rotation handles. Drag the line or center to change spacing, or an end to change angle; Shift snaps rotation to 45°. The original artwork stays in place, the reflected copy previews live, Escape cancels and each drag takes one undo step through `object.repeat.options`. Controls follow zoom/view rotation and hide with edges or the bounding box. Engine and headless canvas regressions passed for gestures, history and visibility; native move, rotation and Escape screenshots were reviewed. [Public mirror-repeat reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-mirror-repeat-options.html). Object › Expand turns radial, grid and mirror repeats into groups of their instances (#1765, #1756).
- Create Strokes uses exact squared Euclidean distances and Guo–Hall thinning in bounded per-blob crops. Maximal-disc pruning, split-crossing merges and degree-two joins simplify its skeleton graph. The whole-blob width rule and ink fitting remain; 46 code-drawn inputs measure ends and junctions in [`image-trace-quality.md`](image-trace-quality.md#centre-lines). Junction degrees measure 44/45 correct in each input variant; remaining spurs are recorded. This is a measured skeleton improvement; the Image Trace score, missing per-feature selection and width controls, and estimates remain unchanged.
Live Paint Gap Options (Object › Live Paint › Gap Options…, `livePaint.gapOptions`): with Gap Detection on, paint stops at the gaps that run from a loose end of a path to the nearest path, up to Small (3 pt), Medium (6 pt), Large (12 pt) or a custom size; Close Gaps With Paths adds an unpainted path across each gap; Preview applies the options live and draws the gaps in the Gap Preview Color. The options are saved, copied and undone with the group, and Merge keeps the bottom group's options when its Gap Detection is on, or else turns Gap Detection on with Small when another merged group has it on. When Gap Detection is off in every merged group, it stays off.
View › Show Live Paint Gaps (`view.livePaintGaps`) draws the gaps of the selected Live Paint groups up to their Paint Stops At size, each in its group's Gap Preview Color, until Hide Live Paint Gaps or Hide Edges hides them ([Live Paint gaps](https://web.archive.org/web/20260105111201/https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/find-and-close-gaps-in-live-paint-groups.html)).
Double-clicking the Live Paint Selection tool opens its Tool Options ([Live Paint Selection tool options](https://web.archive.org/web/20250904012025/https://helpx.adobe.com/illustrator/using/selecting-objects.html)): with Select Fills off, a click selects an edge instead of a face; with Select Strokes off, Alt-click selects a face instead of an edge; Color and Width set the color and width of the highlight on the face or edge under the pointer (red and 4 px by default). The options last across tool switches and are saved with the preferences (`tool.setOption {tool: "livePaintSelection"}`).
Window › Blend opens the Blend panel: Make, the selected blend's Spacing and Orientation (with no blend selected, the options new blends start with), Reverse Front to Back, Reverse Spine, Replace Spine, Expand and Release, each running its Object › Blend command. Make is dimmed until two or more objects are selected. Spacing and Orientation are dimmed until a blend or two or more objects are selected, and the other buttons are dimmed until a blend is selected.
Objects on Path (#2545): a click with the Objects on Path tool (in Edit Toolbar's Modify section and the Blend tool's flyout; Object › Objects on Path › Attach and the context menu with two or more objects selected choose it) attaches the selected objects to the path clicked, open or closed, in an Objects on Path group with the path at its bottom. The objects go along the path column by column from left to right, spread evenly from its first anchor (the last one sits on the end of an open path), each turned to the path at its attachment point; Pivot and Rotate set where each one attaches and how far it tilts (`object.objectsOnPath.attach`, `object.objectsOnPath.options`). Editing the path moves the objects, an object moved on its own goes back, objects moved into or out of the group in the Layers panel join or leave the path, and Object › Objects on Path › Expand leaves a plain group. The group saves and reopens live and exports as plain art. The Space, Move All, Rotate All and Select/Move widgets, Detach, the tool's options dialog and the Properties panel section are missing.