diff --git a/crates/doc/src/style_libs.rs b/crates/doc/src/style_libs.rs index 97d46b7cd..4ecb8948b 100644 --- a/crates/doc/src/style_libs.rs +++ b/crates/doc/src/style_libs.rs @@ -156,6 +156,7 @@ pub const STYLE_LIBRARIES: &[BuiltinStyleLibrary] = &[ BuiltinStyleLibrary { id: "outlines-rules", name: "Outlines and Rules", make: outlines_rules }, BuiltinStyleLibrary { id: "hand-drawn", name: "Hand-Drawn", make: hand_drawn }, BuiltinStyleLibrary { id: "textures", name: "Textures", make: textures }, + BuiltinStyleLibrary { id: "painted-sketched", name: "Painted and Sketched", make: painted_sketched }, BuiltinStyleLibrary { id: "gradient-finishes", name: "Gradient Finishes", make: gradient_finishes }, BuiltinStyleLibrary { id: "shape-effects", name: "Shape Effects", make: shape_effects }, BuiltinStyleLibrary { id: "blends-transparency", name: "Blends and Transparency", make: blends_transparency }, @@ -323,6 +324,59 @@ fn hand_drawn() -> Vec { ] } +/// Painted and Sketched: one catalog paint or sketch effect over the whole appearance, so it +/// repaints the fill and the stroke together (watercolor, dry brush, pastels, charcoal and ink). +fn painted_sketched() -> Vec { + vec![ + style( + "Washed Blue", + vec![F(fill("#8ab9c9"))], + vec![fx("artistic.watercolor", json!({"brushDetail": 8.0, "shadowIntensity": 2.0, "texture": 2.0}))], + ), + style( + "Bristle Ochre", + vec![F(fill("#c4975a")), S(stroke("#76533d", 1.0))], + vec![fx("artistic.dryBrush", json!({"brushSize": 4.0, "brushDetail": 7.0, "texture": 2.0}))], + ), + style( + "Pastel Heather", + vec![F(fill("#a78ca5"))], + vec![fx( + "artistic.roughPastels", + json!({ + "strokeLength": 8.0, + "strokeDetail": 9.0, + "texture": "canvas", + "scaling": 100.0, + "relief": 5.0, + "lightDirection": "topLeft", + "invert": false + }), + )], + ), + style( + "Charcoal Shade", + vec![F(fill("#666666"))], + vec![fx("sketch.charcoal", json!({"thickness": 3.0, "detail": 3.0, "lightDarkBalance": 55.0}))], + ), + style( + "Ink Contour", + vec![F(fill("#d9e2df")), S(stroke("#26323b", 1.0))], + vec![fx("brushStrokes.inkOutlines", json!({"strokeLength": 6.0, "darkIntensity": 24.0, "lightIntensity": 8.0}))], + ), + style( + "Wet Ink", + vec![F(fill("#637b85"))], + vec![fx("brushStrokes.sumiE", json!({"strokeWidth": 7.0, "strokePressure": 5.0, "contrast": 20.0}))], + ), + style( + "Fine Pen", + vec![F(fill("#d8d4c6"))], + vec![fx("sketch.graphicPen", json!({"strokeLength": 6.0, "lightDarkBalance": 55.0, "strokeDirection": "rightDiagonal"}))], + ), + ] +} + /// Textures: fills and strokes painted with the catalog's texture effects (grain, canvas and /// burlap surfaces, craquelure, tiles, patchwork, stained glass and water paper). fn textures() -> Vec { @@ -603,6 +657,36 @@ mod tests { assert_eq!(ids.iter().position(|&i| i == "textures"), ids.iter().position(|&i| i == "hand-drawn").map(|i| i + 1), "after Hand-Drawn"); } + #[test] + fn painted_and_sketched_follows_textures_with_one_effect_per_style() { + let ids: Vec<&str> = STYLE_LIBRARIES.iter().map(|b| b.id).collect(); + let at = ids.iter().position(|&id| id == "painted-sketched").expect("listed"); + assert_eq!(ids[at - 1], "textures"); + let lib = builtin_style_library("painted-sketched").unwrap(); + assert_eq!(lib.name, "Painted and Sketched"); + let names: Vec<&str> = lib.styles.iter().map(|g| g.name.as_str()).collect(); + assert_eq!(names, ["Washed Blue", "Bristle Ochre", "Pastel Heather", "Charcoal Shade", "Ink Contour", "Wet Ink", "Fine Pen"]); + let effects: Vec<&str> = lib.styles.iter().flat_map(|g| g.appearance.effects.iter().map(|e| e.id.as_str())).collect(); + assert_eq!( + effects, + [ + "artistic.watercolor", + "artistic.dryBrush", + "artistic.roughPastels", + "sketch.charcoal", + "brushStrokes.inkOutlines", + "brushStrokes.sumiE", + "sketch.graphicPen" + ] + ); + assert!(lib.styles.iter().all(|g| g.appearance.items.iter().all(|i| i.effects().is_empty())), "no effects on a fill or stroke"); + // No other built-in library holds a style of the same name. + for b in STYLE_LIBRARIES.iter().filter(|b| b.id != "painted-sketched") { + let other = builtin_style_library(b.id).unwrap(); + assert!(other.styles.iter().all(|g| !names.contains(&g.name.as_str())), "{}", b.id); + } + } + #[test] fn vcstyles_round_trip() { let mut lib = (*builtin_style_library("gradient-finishes").unwrap()).clone(); diff --git a/crates/engine/src/tests/stylelib.rs b/crates/engine/src/tests/stylelib.rs index d1d415590..fd8f2c7e2 100644 --- a/crates/engine/src/tests/stylelib.rs +++ b/crates/engine/src/tests/stylelib.rs @@ -87,6 +87,87 @@ fn builtin_libraries_are_listed_with_original_styles_using_catalogued_effects() assert!(s.execute("graphicStyle.library", &json!({"library": "nope"})).is_err()); } +#[test] +fn painted_and_sketched_styles_set_every_effect_setting_and_reopen_applied() { + let mut s = session(); + let list = run(&mut s, "graphicStyle.libraries", json!({})); + let libs = list["libraries"].as_array().unwrap(); + let at = libs.iter().position(|l| l["id"] == "painted-sketched").expect("listed"); + assert_eq!((libs[at]["name"].as_str(), libs[at]["count"].as_u64()), (Some("Painted and Sketched"), Some(7))); + assert_eq!(libs[at - 1]["id"], "textures"); + let g = run(&mut s, "graphicStyle.library", json!({"library": "Painted and Sketched"})); + let listed: Vec<(&str, &str, Vec<&str>)> = + g["styles"].as_array().unwrap().iter().map(|x| (x["name"].as_str().unwrap(), x["fill"].as_str().unwrap(), names(&x["effects"]))).collect(); + let want = [ + ("Washed Blue", "#8ab9c9", "artistic.watercolor"), + ("Bristle Ochre", "#c4975a", "artistic.dryBrush"), + ("Pastel Heather", "#a78ca5", "artistic.roughPastels"), + ("Charcoal Shade", "#666666", "sketch.charcoal"), + ("Ink Contour", "#d9e2df", "brushStrokes.inkOutlines"), + ("Wet Ink", "#637b85", "brushStrokes.sumiE"), + ("Fine Pen", "#d8d4c6", "sketch.graphicPen"), + ]; + assert_eq!(listed, want.iter().map(|&(n, f, e)| (n, f, vec![e])).collect::>()); + // Every setting of each effect is given, with the catalog's key and kind of value. + let (_, lib) = cmd::stylelib::library(&s, "painted-sketched").unwrap(); + for st in &lib.styles { + let e = &st.appearance.effects[0]; + let defaults = vectorcraft_render::effects::effect_info(&e.id).unwrap().defaults; + let (set, all) = (e.params.as_object().unwrap(), defaults.as_object().unwrap()); + let keys = |m: &serde_json::Map| m.keys().cloned().collect::>(); + assert_eq!(keys(set), keys(all), "{}", st.name); + for (k, v) in set { + let kind = |v: &Value| (v.is_number(), v.is_string(), v.is_boolean()); + assert_eq!(kind(v), kind(&all[k]), "{} / {k}", st.name); + } + } + let strokes: Vec<(&str, f64)> = + lib.styles.iter().filter(|g| g.appearance.stroke_paint() != Paint::None).map(|g| (g.name.as_str(), g.appearance.stroke_width())).collect(); + assert_eq!(strokes, [("Bristle Ochre", 1.0), ("Ink Contour", 1.0)]); + + // Each one adds and applies to a rectangle in one undo step. + let mut ids = vec![]; + for (i, (name, fill, effect)) in want.iter().enumerate() { + let id = rect(&mut s, 10.0 + 25.0 * i as f64, 20.0, 20.0, 30.0); + let before = undo_len(&s); + let r = run(&mut s, "graphicStyle.addFromLibrary", json!({"library": "painted-sketched", "name": name, "apply": true, "ids": [id.0]})); + assert_eq!((names(&r["added"]), r["applied"].as_str()), (vec![*name], Some(*name))); + assert_eq!(undo_len(&s), before + 1, "{name}"); + let node = doc(&s).node(id).unwrap(); + assert_eq!(node.appearance.fill_paint(), Paint::solid(vectorcraft_color::Color::from_hex(fill).unwrap()), "{name}"); + assert_eq!(node.appearance.effects.iter().map(|e| e.id.as_str()).collect::>(), [*effect]); + assert_eq!(node.appearance.effects[0].params, lib.style(name).unwrap().appearance.effects[0].params, "{name}"); + ids.push((id, *name)); + } + // Adding one again reuses it: no new style, no undo step. + let before = (undo_len(&s), doc(&s).graphic_styles.len()); + let r = run(&mut s, "graphicStyle.addFromLibrary", json!({"library": "painted-sketched", "name": "Wet Ink"})); + assert_eq!((names(&r["added"]), names(&r["existing"])), (vec![], vec!["Wet Ink"])); + assert_eq!((undo_len(&s), doc(&s).graphic_styles.len()), before); + // An unknown style or library changes nothing. + assert!(s.execute("graphicStyle.addFromLibrary", &json!({"library": "painted-sketched", "names": ["Wet Ink", "Nope"]})).is_err()); + assert!(s.execute("graphicStyle.addFromLibrary", &json!({"library": "painted-and-sketched", "name": "Wet Ink"})).is_err()); + assert_eq!((undo_len(&s), doc(&s).graphic_styles.len()), before); + + // Saved and opened again, the styles keep their effects and the rectangles stay linked to them. + let b64 = run(&mut s, "document.serialize", json!({}))["dataBase64"].as_str().unwrap().to_string(); + run(&mut s, "document.open", json!({"name": "painted.vectorcraft", "dataBase64": b64})); + for (id, name) in &ids { + let d = doc(&s); + let style = d.graphic_style(name).unwrap(); + assert!(style.same_look(lib.style(name).unwrap()), "{name}"); + let node = d.node(*id).unwrap(); + assert_eq!((node.extras().graphic_style, &node.appearance.effects), (Some(style.id), &style.appearance.effects), "{name}"); + } + + // With nothing selected, `apply` only adds; empty `names` adds the whole library in order. + run(&mut s, "file.new", json!({"width": 100, "height": 100})); + let r = run(&mut s, "graphicStyle.addFromLibrary", json!({"library": "painted-sketched", "name": "Fine Pen", "apply": true})); + assert!(r.get("applied").is_none() && doc(&s).graphic_style("Fine Pen").is_some()); + let r = run(&mut s, "graphicStyle.addFromLibrary", json!({"library": "painted-sketched", "names": []})); + assert_eq!((names(&r["added"]), names(&r["existing"])), (want[..6].iter().map(|w| w.0).collect::>(), vec!["Fine Pen"])); +} + #[test] fn add_dedupes_names_as_one_undo_step() { let mut s = session(); diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 2c7632032..f59292f1d 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -1399,6 +1399,14 @@ font-technology Variable Променливи Hair Space Тънък интервал Hand Tool Инструмент „Ръка“ Hand-Drawn Ръчно рисувани + Painted and Sketched Рисувани и скицирани + Washed Blue Размито синьо + Bristle Ochre Охра с четка + Pastel Heather Пастелен пирен + Charcoal Shade Сянка с въглен + Ink Contour Мастилен контур + Wet Ink Мокро мастило + Fine Pen Фино перо Handles Дръжки Hard Light Твърда светлина Hard Mix Твърдо смесване diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index 4068608d5..984ee236a 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -812,6 +812,14 @@ Damp Paper Vlažni papir Outlines and Rules Obrisi i linije Hand-Drawn Ručno crtano + Painted and Sketched Slikano i skicirano + Washed Blue Isprano plavo + Bristle Ochre Oker kistom + Pastel Heather Pastelni vrijesak + Charcoal Shade Sjena ugljenom + Ink Contour Kontura tušem + Wet Ink Mokri tuš + Fine Pen Fino pero Gradient Finishes Gradijentne završnice Shape Effects Efekti oblika Blends and Transparency Stapanja i prozirnost diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index 92c8ee72f..e74080a19 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -1907,6 +1907,14 @@ digits Malayalam Malaiàlam Hand Mà Hand Tool Eina de mà Hand-Drawn Dibuixat a mà + Painted and Sketched Pintat i esbossat + Washed Blue Blau rentat + Bristle Ochre Ocre de cerres + Pastel Heather Bruc pastel + Charcoal Shade Ombra de carbó + Ink Contour Contorn de tinta + Wet Ink Tinta humida + Fine Pen Ploma fina Handles Nanses Hard Dur Hard Light Llum dura diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index f82b69c36..33caea057 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -821,6 +821,14 @@ Damp Paper Vlhký papír Outlines and Rules Obrysy a linky Hand-Drawn Ručně kreslené + Painted and Sketched Malované a skicované + Washed Blue Rozmytá modrá + Bristle Ochre Okr štětcem + Pastel Heather Pastelový vřes + Charcoal Shade Stín uhlem + Ink Contour Obrys tuší + Wet Ink Mokrá tuš + Fine Pen Jemné pero Gradient Finishes Přechodové povrchy Shape Effects Efekty tvarů Blends and Transparency Prolnutí a průhlednost diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 3b4d8b7dd..2e7a05827 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -758,6 +758,14 @@ Damp Paper Fugtigt papir Outlines and Rules Konturer og linjer Hand-Drawn Håndtegnet + Painted and Sketched Malet og skitseret + Washed Blue Udvasket blå + Bristle Ochre Børsteokker + Pastel Heather Pastellyng + Charcoal Shade Kulskygge + Ink Contour Blækkontur + Wet Ink Vådt blæk + Fine Pen Fin pen Gradient Finishes Gradientfinish Shape Effects Formeffekter Blends and Transparency Blandinger og gennemsigtighed diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index c06f55f1f..1c0310c09 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -1444,6 +1444,14 @@ file-filter Templates Vorlagen Hair Space Haarspatium Hand Tool Hand-Werkzeug Hand-Drawn Handgezeichnet + Painted and Sketched Gemalt und skizziert + Washed Blue Verwaschenes Blau + Bristle Ochre Borstenocker + Pastel Heather Pastellheide + Charcoal Shade Kohleschatten + Ink Contour Tuschekontur + Wet Ink Nasse Tusche + Fine Pen Feine Feder Handles Griffe Hard Light Hartes Licht Hard Mix Hart mischen diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index b35359e5a..6da932071 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -1374,6 +1374,14 @@ font-technology Variable Μεταβλητές Hair Space Πολύ λεπτό διάστημα Hand Tool Χέρι (Hand Tool) Hand-Drawn Σχεδιασμένο στο χέρι + Painted and Sketched Ζωγραφισμένο και σκιτσαρισμένο + Washed Blue Ξεπλυμένο μπλε + Bristle Ochre Ώχρα με πινέλο + Pastel Heather Παστέλ ρείκι + Charcoal Shade Σκιά με κάρβουνο + Ink Contour Περίγραμμα με μελάνι + Wet Ink Υγρό μελάνι + Fine Pen Λεπτή πένα Handles Λαβές Hard Light Έντονο φως Hard Mix Έντονη ανάμειξη diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 1625e212b..04e22cc19 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -1453,6 +1453,14 @@ font-technology Variable Variables Hair Space Espacio fino Hand Tool Herramienta Mano Hand-Drawn Dibujado a mano + Painted and Sketched Pintado y bocetado + Washed Blue Azul lavado + Bristle Ochre Ocre de cerdas + Pastel Heather Brezo pastel + Charcoal Shade Sombra al carboncillo + Ink Contour Contorno de tinta + Wet Ink Tinta húmeda + Fine Pen Plumilla fina Handles Manejadores Hard Light Luz fuerte Hard Mix Mezcla definida diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index 9222b7a47..6b169cba6 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -812,6 +812,14 @@ Damp Paper Kostea paperi Outlines and Rules Ääriviivat ja viivat Hand-Drawn Käsin piirretyt + Painted and Sketched Maalatut ja luonnostellut + Washed Blue Haalea sininen + Bristle Ochre Harjasokra + Pastel Heather Pastellikanerva + Charcoal Shade Hiilivarjo + Ink Contour Mustekontuuri + Wet Ink Märkä muste + Fine Pen Hieno kynä Gradient Finishes Liukuväriviimeistelyt Shape Effects Muototehosteet Blends and Transparency Sulautukset ja läpinäkyvyys diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 751d14e47..e75b26b52 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -1447,6 +1447,14 @@ font-technology Variable Variables Hair Space Espace ultrafine Hand Tool Outil Main Hand-Drawn Dessiné à la main + Painted and Sketched Peint et esquissé + Washed Blue Bleu lavé + Bristle Ochre Ocre à la brosse + Pastel Heather Bruyère pastel + Charcoal Shade Ombre au fusain + Ink Contour Contour encré + Wet Ink Encre humide + Fine Pen Plume fine Handles Poignées Hard Light Lumière crue Hard Mix Mélange maximal diff --git a/crates/ui-egui/src/i18n/hi.tsv b/crates/ui-egui/src/i18n/hi.tsv index 281352f29..a12ccdc59 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -2101,6 +2101,14 @@ antiAlias Sharp शार्प Hand हैंड Hand Tool हैंड टूल Hand-Drawn हाथ-ड्रन + Painted and Sketched पेंट और स्केच + Washed Blue धुला नीला + Bristle Ochre ब्रश गेरू + Pastel Heather पेस्टल बैंगनी + Charcoal Shade चारकोल छाया + Ink Contour स्याही रूपरेखा + Wet Ink गीली स्याही + Fine Pen बारीक पेन Handles हैंडल्स Hard कठोर Hard Light हार्ड लाइट diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 312261e06..a5fa294e5 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -1847,6 +1847,14 @@ file-filter Templates Sablonok Hand Kéz Hand Tool Kéz Hand-Drawn Kézzel rajzolt + Painted and Sketched Festett és vázlatos + Washed Blue Kimosott kék + Bristle Ochre Sörteokker + Pastel Heather Pasztell hanga + Charcoal Shade Szénárnyék + Ink Contour Tuskontúr + Wet Ink Nedves tus + Fine Pen Finom toll Handles Fogantyúk Hard Szigorú Hard Light Kemény fény diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index cbfcba5b4..b8bd9ef91 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -758,6 +758,14 @@ Damp Paper Kertas Basah Outlines and Rules Garis luar dan garis Hand-Drawn Gambar tangan + Painted and Sketched Lukisan dan sketsa + Washed Blue Biru luntur + Bristle Ochre Oker kuas kering + Pastel Heather Ungu pastel + Charcoal Shade Bayangan arang + Ink Contour Kontur tinta + Wet Ink Tinta basah + Fine Pen Pena halus Gradient Finishes Finishing gradien Shape Effects Efek bentuk Blends and Transparency Blend dan transparansi diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index ea0b33b05..570f75311 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -1474,6 +1474,14 @@ file-filter Templates Modelli Hair Space Spazio sottilissimo Hand Tool Strumento Mano Hand-Drawn Disegnato a mano + Painted and Sketched Dipinto e schizzato + Washed Blue Blu slavato + Bristle Ochre Ocra a setole + Pastel Heather Erica pastello + Charcoal Shade Ombra a carboncino + Ink Contour Contorno a china + Wet Ink China bagnata + Fine Pen Pennino fine Handles Maniglie Hard Light Luce intensa Hard Mix Miscela intensa diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index a9ecf561b..8dee3e7fc 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -844,6 +844,14 @@ digits Malayalam マラヤーラム数字 Damp Paper 湿った紙 Outlines and Rules アウトラインと罫線 Hand-Drawn 手描き風 + Painted and Sketched 絵画とスケッチ + Washed Blue にじみブルー + Bristle Ochre 筆跡オーカー + Pastel Heather パステルヘザー + Charcoal Shade 木炭シェード + Ink Contour インク輪郭 + Wet Ink 濡れ墨 + Fine Pen 細いペン Gradient Finishes グラデーション仕上げ Shape Effects シェイプ効果 Blends and Transparency ブレンドと透明 diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index db1b6dc4b..de00821d8 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -718,6 +718,14 @@ Damp Paper ნესტიანი ქაღალდი Outlines and Rules კონტურები და ხაზები Hand-Drawn ხელით დახატული + Painted and Sketched დახატული და ესკიზური + Washed Blue გარეცხილი ლურჯი + Bristle Ochre ფუნჯის ოხრა + Pastel Heather პასტელის იისფერი + Charcoal Shade ნახშირის ჩრდილი + Ink Contour მელნის კონტური + Wet Ink სველი მელანი + Fine Pen წვრილი კალამი Gradient Finishes გრადიენტული ზედაპირები Shape Effects ფიგურის ეფექტები Blends and Transparency გადასვლები და გამჭვირვალობა diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 673e28963..43ff676b0 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -837,6 +837,14 @@ digits Malayalam 말라얄람어 Damp Paper 젖은 종이 Outlines and Rules 윤곽선과 괘선 Hand-Drawn 손그림 + Painted and Sketched 페인팅과 스케치 + Washed Blue 번진 파랑 + Bristle Ochre 붓결 황토 + Pastel Heather 파스텔 헤더 + Charcoal Shade 목탄 음영 + Ink Contour 잉크 윤곽 + Wet Ink 젖은 먹 + Fine Pen 가는 펜 Gradient Finishes 그레이디언트 마감 Shape Effects 모양 효과 Blends and Transparency 블렌드와 투명도 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 893a715b9..379accb30 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -772,6 +772,14 @@ Damp Paper Vochtig papier Outlines and Rules Omtrekken en lijnen Hand-Drawn Handgetekend + Painted and Sketched Geschilderd en geschetst + Washed Blue Verwassen blauw + Bristle Ochre Borsteloker + Pastel Heather Pastelheide + Charcoal Shade Houtskoolschaduw + Ink Contour Inktcontour + Wet Ink Natte inkt + Fine Pen Fijne pen Gradient Finishes Verloopafwerkingen Shape Effects Vormeffecten Blends and Transparency Vermengingen en transparantie diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 5d06e8ac1..14e7c0e1c 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -768,6 +768,14 @@ Damp Paper Wilgotny papier Outlines and Rules Kontury i linie Hand-Drawn Rysowane odręcznie + Painted and Sketched Malowane i szkicowane + Washed Blue Rozmyty błękit + Bristle Ochre Ochra pędzlem + Pastel Heather Pastelowy wrzos + Charcoal Shade Cień węglem + Ink Contour Kontur tuszem + Wet Ink Mokry tusz + Fine Pen Cienkie pióro Gradient Finishes Wykończenia gradientowe Shape Effects Efekty kształtów Blends and Transparency Przejścia i przezroczystość diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 671486589..9ed614199 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -1278,6 +1278,14 @@ antiAlias Sharp Nítido Hair Space Espaço Fino Hand Tool Ferramenta Mão Hand-Drawn Desenhado à Mão + Painted and Sketched Pintado e Esboçado + Washed Blue Azul Lavado + Bristle Ochre Ocre de Cerdas + Pastel Heather Urze Pastel + Charcoal Shade Sombra a Carvão + Ink Contour Contorno a Nanquim + Wet Ink Nanquim Úmido + Fine Pen Pena Fina Handles Alças Hard Light Luz Forte Hard Mix Mistura definida diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 0a96cd524..bffbf99e1 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -1463,6 +1463,14 @@ file-filter Templates Шаблоны Hair Space Волосяная шпация Hand Tool Инструмент «Рука» Hand-Drawn Нарисованный от руки + Painted and Sketched Живопись и эскизы + Washed Blue Размытый синий + Bristle Ochre Охра кистью + Pastel Heather Пастельный вереск + Charcoal Shade Тень углём + Ink Contour Контур тушью + Wet Ink Влажная тушь + Fine Pen Тонкое перо Handles Маркеры Hard Light Жёсткий свет Hard Mix Жёсткое наложение diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 1ea596286..d3699caf8 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -1412,6 +1412,14 @@ font-technology Variable Variabla Hair Space Hårfint blanksteg Hand Tool Handverktyg Hand-Drawn Handritad + Painted and Sketched Målat och skissat + Washed Blue Urtvättad blå + Bristle Ochre Borstockra + Pastel Heather Pastelljung + Charcoal Shade Kolskugga + Ink Contour Tuschkontur + Wet Ink Våt tusch + Fine Pen Fin penna Handles Handtag Hard Light Hårt ljus Hard Mix Hård blandning diff --git a/crates/ui-egui/src/i18n/tests.rs b/crates/ui-egui/src/i18n/tests.rs index be5c132db..ade1efa8b 100644 --- a/crates/ui-egui/src/i18n/tests.rs +++ b/crates/ui-egui/src/i18n/tests.rs @@ -238,6 +238,20 @@ fn anti_aliasing_method_labels_are_in_complete_catalogs() { } } +/// The Painted and Sketched graphic style library's name and its styles' names have a row in every +/// bundled catalog. +#[test] +fn painted_and_sketched_style_names_are_in_every_catalog() { + let lib = vectorcraft_doc::style_libs::builtin_style_library("painted-sketched").expect("built in"); + let labels: Vec<&str> = std::iter::once(lib.name.as_str()).chain(lib.styles.iter().map(|g| g.name.as_str())).collect(); + assert_eq!(labels.len(), 8); + for lang in LANGUAGES.iter().filter(|lang| !lang.source.is_empty()) { + for label in &labels { + assert!(lang.catalog().plain(label).is_some(), "{} is missing {label}", lang.code); + } + } +} + /// The names the EPS options' menus show (preview, overprints, flattener preset and PostScript /// level), and Save As's Preview label. #[test] diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index f8951a65b..e1577c728 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -1376,6 +1376,14 @@ font-technology Variable Değişken Hair Space Saç Alanı Hand Tool El Aracı Hand-Drawn elle çizilmiş + Painted and Sketched Boyanmış ve Eskiz + Washed Blue Soluk Mavi + Bristle Ochre Fırça Darbeli Toprak Sarısı + Pastel Heather Pastel Funda + Charcoal Shade Kömür Gölge + Ink Contour Mürekkep Kontur + Wet Ink Islak Mürekkep + Fine Pen İnce Kalem Handles Kollar Hard Light Sert Işık Harmony Rules: Uyum Kuralları: diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 053cff5bf..237a58564 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -1468,6 +1468,14 @@ file-filter Templates Шаблони Hair Space Волосяна шпація Hand Tool Інструмент «Рука» Hand-Drawn Намальований від руки + Painted and Sketched Мальовані та ескізні + Washed Blue Розмитий синій + Bristle Ochre Охра пензлем + Pastel Heather Пастельний верес + Charcoal Shade Тінь вугіллям + Ink Contour Контур тушшю + Wet Ink Мокра туш + Fine Pen Тонке перо Handles Маркери Hard Light Жорстке світло Hard Mix Жорстке накладання diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 9acef06d8..ba52950f3 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -1695,6 +1695,14 @@ file-filter Templates Mẫu Halo Vầng hào quang Hand Tool Công cụ Bàn tay Hand-Drawn Vẽ tay + Painted and Sketched Vẽ màu và phác thảo + Washed Blue Xanh nhạt nhòa + Bristle Ochre Vàng đất cọ khô + Pastel Heather Tím phấn màu + Charcoal Shade Bóng than + Ink Contour Đường viền mực + Wet Ink Mực ướt + Fine Pen Bút mảnh Handles Tay nắm Hard Cứng Hard Light Ánh sáng mạnh diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index b5c751c20..1df45db2a 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -1403,6 +1403,14 @@ file-filter Templates 模板 Hair Space 极细空格 Hand Tool 抓手工具 Hand-Drawn 手绘 + Painted and Sketched 绘画与素描 + Washed Blue 水洗蓝 + Bristle Ochre 干刷土黄 + Pastel Heather 粉彩淡紫 + Charcoal Shade 炭笔阴影 + Ink Contour 墨线轮廓 + Wet Ink 湿墨 + Fine Pen 细笔 Handles 手柄 Hard Light 强光 Hard Mix 实色混合 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 169f070e5..ff07c51b4 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -1439,6 +1439,14 @@ file-filter Templates 範本 Hair Space 極細空格 Hand Tool 手形工具 Hand-Drawn 手繪 + Painted and Sketched 繪畫與素描 + Washed Blue 水洗藍 + Bristle Ochre 乾刷土黃 + Pastel Heather 粉彩淡紫 + Charcoal Shade 炭筆陰影 + Ink Contour 墨線輪廓 + Wet Ink 濕墨 + Fine Pen 細筆 Handles 控制把手 Hard Light 實光 Hard Mix 實色混合 diff --git a/crates/ui-egui/src/panels/tests_style_libraries.rs b/crates/ui-egui/src/panels/tests_style_libraries.rs index a77681938..46c274dee 100644 --- a/crates/ui-egui/src/panels/tests_style_libraries.rs +++ b/crates/ui-egui/src/panels/tests_style_libraries.rs @@ -101,6 +101,30 @@ fn clicking_a_library_style_adds_and_applies_it_in_one_step() { assert!(last_open(&app.ui).is_none()); } +#[test] +fn painted_and_sketched_opens_from_the_window_menu_and_a_click_applies_its_style() { + let mut app = app(); + let entry = crate::menus::menu_entries(&app) + .into_iter() + .find(|e| e.path == ["Window", "Graphic Style Libraries"] && e.label == "Painted and Sketched") + .expect("a Window menu item"); + assert_eq!((entry.command.as_deref(), &entry.params["library"]), (Some("window.graphicStyleLibrary"), &json!("painted-sketched"))); + let rect = app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 80, "height": 40})).unwrap()["id"].as_u64().unwrap(); + app.run(entry.command.as_deref().unwrap(), entry.params.clone()).unwrap(); + assert_eq!(last_open(&app.ui).map(|p| (p.kind.as_str(), p.id.as_str())), Some(("graphicStyles", "painted-sketched"))); + let ctx = context(); + let undo = undo_len(&app); + click(&mut app, &ctx, "Charcoal Shade", Modifiers::NONE, 0.0); + let (g, linked) = app.session.selection_graphic_style().expect("applied to the selection"); + assert_eq!((g.name.as_str(), linked), ("Charcoal Shade", true)); + assert_eq!(undo_len(&app), undo + 1, "added and applied as one undo step"); + let node = app.session.doc().unwrap().doc.node(vectorcraft_doc::NodeId(rect)).unwrap().appearance.effects.clone(); + assert_eq!(node.iter().map(|e| e.id.as_str()).collect::>(), ["sketch.charcoal"]); + let lib = GraphicStyleLibraries::get(&app, "painted-sketched").unwrap().1; + let rows = GraphicStyleLibraries::rows(&lib, "ink"); + assert_eq!(rows.iter().map(|r| r.name).collect::>(), ["Ink Contour", "Wet Ink"]); +} + #[test] fn the_window_menu_and_the_panel_open_every_library() { let mut app = app(); diff --git a/docs/gaps.md b/docs/gaps.md index 4186fa64d..612171d10 100644 --- a/docs/gaps.md +++ b/docs/gaps.md @@ -645,12 +645,14 @@ macOS cursor delivery (#2292) is narrowed to a native host adapter using the exi Direct Selection of a path, compound path or type inside a dynamic instance also takes opacity and blend (`transparency.set`) and a gradient or pattern fill or stroke (`paint.setFill` / `paint.setStroke`, Eyedropper), for that instance only; - graphic style libraries (Window › Graphic Style Libraries; nine original libraries generated in code, including + graphic style libraries (Window › Graphic Style Libraries; ten original libraries generated in code, including Game HUD with Health Fill, Mana Glow, Damage Flash, Quest Outline, Coin Shine and Boss Alert, Status Effects - with Poison Tint, Freeze Edge, Burn Aura, Stun Pulse, Stealth Fade and Shield Buff, and Textures with Fine Grain, - Canvas Weave, Burlap Sack, Cracked Glaze, Mosaic Floor, Quilted Squares, Leaded Glass and Damp Paper, painted with - the texture effects; User Defined; Other Library…; `graphicStyle.libraries`, `graphicStyle.addFromLibrary`). -- **Missing:** more built-in brush libraries beyond the six shipped (and further graphic-style libraries beyond the nine shipped, and further symbol libraries beyond Basic + with Poison Tint, Freeze Edge, Burn Aura, Stun Pulse, Stealth Fade and Shield Buff, Painted and Sketched with + Washed Blue, Bristle Ochre, Pastel Heather, Charcoal Shade, Ink Contour, Wet Ink and Fine Pen, and Textures with + Fine Grain, Canvas Weave, Burlap Sack, Cracked Glaze, Mosaic Floor, Quilted Squares, Leaded Glass and Damp Paper, + painted with the texture effects; User Defined; Other Library…; `graphicStyle.libraries`, + `graphicStyle.addFromLibrary`). +- **Missing:** more built-in brush libraries beyond the six shipped (and further graphic-style libraries beyond the ten shipped, and further symbol libraries beyond Basic Shapes, UI Frames, Map Markers, Inventory Icons and Skill Icons; original, generated in code, never Adobe's); dynamic symbols' `.ai` read and write; Global Edit of groups holding type or images, rotated copies as matches and highlighting matches before the start; 9-slice guides outside the symbol's art (a dragged guide stops at the art's edge). diff --git a/docs/mcp.md b/docs/mcp.md index 1c5f9e318..cd4380443 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -3402,7 +3402,7 @@ as a value left out. ## Graphic style libraries Graphic style libraries are read-only sets of graphic styles generated in code (Shadows and Glows, Outlines and -Rules, Hand-Drawn, Textures, Gradient Finishes, Shape Effects, Blends and Transparency, Game HUD, Status Effects). Textures +Rules, Hand-Drawn, Textures, Painted and Sketched, Gradient Finishes, Shape Effects, Blends and Transparency, Game HUD, Status Effects). Textures (`textures`) paints its fills and strokes with the texture effects: Fine Grain (`texture.grain`), Canvas Weave and Burlap Sack (`texture.texturizer`), Cracked Glaze (`texture.craquelure`), Mosaic Floor (`texture.mosaicTiles`), Quilted Squares (`texture.patchwork`), Leaded Glass (`texture.stainedGlass`) and Damp Paper (`sketch.waterPaper`). `graphicStyle.libraries` lists them @@ -3415,6 +3415,13 @@ the patterns the styles paint with come along. `apply: true` also applies the fi does. Library styles keep placed gradients in unit-box space, so each object gets them at the same place relative to its bounds. +Painted and Sketched (`painted-sketched`) holds Washed Blue, Bristle Ochre, Pastel Heather, Charcoal Shade, Ink +Contour, Wet Ink and Fine Pen. Each is a solid fill (Bristle Ochre and Ink Contour add a 1 pt stroke) under one object +effect that repaints fill and stroke together: `artistic.watercolor`, `artistic.dryBrush`, `artistic.roughPastels`, +`sketch.charcoal`, `brushStrokes.inkOutlines`, `brushStrokes.sumiE` and `sketch.graphicPen`, in that order. The +colors and effect settings are VectorCraft's own. These are raster effects, so the document's raster effects +resolution sets how fine they draw. + ```json {"name":"run_command","arguments":{"command":"graphicStyle.library","params":{"library":"hand-drawn"}}} {"name":"run_command","arguments":{"command":"graphicStyle.addFromLibrary","params":{"library":"hand-drawn","name":"Crosshatch","apply":true}}} diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 707cc7f5b..58eadca16 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1370,7 +1370,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - **Transparency:** three-state Knockout Group (on, neutral, off), Opacity & Mask Define Knockout Shape, Page Isolated Blending and Page Knockout Group, on screen and in SVG/PDF. SVG export writes a knockout group of opaque art plainly, without masks, so shapes meeting edge to edge leave no seam; translucent, blended, masked or brushed art knocks out through masks. Blend modes match the standard formulas on screen, in SVG and in PDF, and non-isolated groups blend with the art below. Flatten Transparency with presets, a dialog and a Flattener Preview panel. Flattening for files without transparency (PDF/X-1a, PDF/X-3) in a CMYK document keeps the inks where it rasterizes, as under an image with see-through pixels: the images it makes hold ink amounts, not screen colours the file would separate again, so a C100 M6 Y41 K3 panel stays C100 M6 Y41 K3 there (#2160, `engine/src/cmd/fileio/tests_pdfx.rs`). On the canvas, the renderer no longer draws the picture of the art below back as an image each time it copies the backdrop of a non-isolated group that blends: it keeps that picture and draws the art that follows over it. At 2880 × 1800, 50 translucent groups with Multiply members take 65 ms a frame instead of 299 ms on 8 threads (`vectorcraft-cli bench`), and the frames have the same pixels. Type inside a group, layer, symbol instance, blend or repeat keeps its opacity, blend mode, opacity mask, Opacity & Mask Define Knockout Shape, own effects, perspective and visibility when the effects on the container outline it. Hidden type with a geometry effect of its own is left out of exports. - - **Graphic styles:** styles keep opacity and blend mode, capture groups and type, apply on top with Alt, and link to the objects using them; Redefine, Break Link, Graphic Style Options, Select All Unused, Sort by Name, and Select > Same Graphic Style / Appearance Attribute. The panel has list and thumbnail views with rendered previews, merge, reorder and drag and drop, plus a Control bar Style picker; nine generated style libraries (including Game HUD, Status Effects and Textures) save and load as `.vcstyles`. + - **Graphic styles:** styles keep opacity and blend mode, capture groups and type, apply on top with Alt, and link to the objects using them; Redefine, Break Link, Graphic Style Options, Select All Unused, Sort by Name, and Select > Same Graphic Style / Appearance Attribute. The panel has list and thumbnail views with rendered previews, merge, reorder and drag and drop, plus a Control bar Style picker; ten generated style libraries (including Game HUD, Status Effects, Painted and Sketched, whose seven styles each repaint a solid fill with one watercolor, dry brush, pastel, charcoal or ink effect, and Textures) save and load as `.vcstyles`. The built-in Textures graphic style library (`graphicStyle.addFromLibrary {library: "textures"}`) has eight styles that paint their fills and strokes with the texture effects: Fine Grain (Grain), Canvas Weave and Burlap Sack (Texturizer), Cracked Glaze (Craquelure), Mosaic Floor (Mosaic Tiles), Quilted Squares (Patchwork), Leaded Glass (Stained Glass) and Damp Paper (Water Paper). The help page names new graphic styles for textures ([What's new](https://helpx.adobe.com/illustrator/desktop/new-features/whats-new.html)) without naming them, so the styles, their names and their settings are VectorCraft's own (`tests::stylelib::textures_styles_apply_their_texture_effects_and_keep_their_params_through_save_and_open`). In the Graphic Styles panel, Shift-click selects a range of styles and Ctrl/Cmd-click adds or removes one. Applying a style to several objects, and Redefine Graphic Style, give the objects one shared copy of the style's appearance instead of a copy each; an object that is edited later gets its own copy, and an object whose placed gradients are fitted to its own bounds gets its own. The undo history's memory estimate counts a shared appearance once. Saved files are unchanged (`crates/engine/src/tests/styles.rs`).