mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 17:56:50 -06:00
committed by
bflatastic
parent
fd780c51b0
commit
2ff683d5fe
@@ -482,6 +482,13 @@ pub fn effect_catalog() -> Vec<EffectInfo> {
|
||||
"{chalkArea: 0..20 (6; chalk in light areas), charcoalArea: 0..20 (6; charcoal in dark areas), strokePressure: 0..5 (2)} redraws the object with chalk highlights and charcoal strokes on white paper, keeping its coverage. Original visual approximation from public documentation; slider ranges and defaults are VectorCraft choices, not verified proprietary values.",
|
||||
json!({"chalkArea": 6.0, "charcoalArea": 6.0, "strokePressure": 2.0}),
|
||||
),
|
||||
r(
|
||||
"sketch.chrome",
|
||||
"Chrome…",
|
||||
SKETCH,
|
||||
"{detail: 0..10 (4; finer surface texture), smoothness: 0..10 (4; softer relief before the highlights and shadows)} treats the object as polished chrome lit from the top left: bright highlights on high points and dark shadows in low ones, keeping its coverage. Original visual approximation; chosen VectorCraft defaults.",
|
||||
json!({"detail": 4.0, "smoothness": 4.0}),
|
||||
),
|
||||
r(
|
||||
"sketch.graphicPen",
|
||||
"Graphic Pen…",
|
||||
|
||||
@@ -41,7 +41,7 @@ pub use sketch::{HALFTONE_TYPES, HalftonePattern};
|
||||
pub use texture::{GRAIN_TYPES, Grain, LIGHT_DIRECTIONS, Light, TEXTURES, Texture};
|
||||
|
||||
/// The Photoshop-style effect ids (all raster effects, see [`crate::is_raster`]).
|
||||
pub const PIXEL_EFFECTS: [&str; 36] = [
|
||||
pub const PIXEL_EFFECTS: [&str; 37] = [
|
||||
"artistic.cutout",
|
||||
"artistic.filmGrain",
|
||||
"artistic.neonGlow",
|
||||
@@ -66,6 +66,7 @@ pub const PIXEL_EFFECTS: [&str; 36] = [
|
||||
"pixelate.pointillize",
|
||||
"sharpen.unsharpMask",
|
||||
"sketch.chalkCharcoal",
|
||||
"sketch.chrome",
|
||||
"sketch.graphicPen",
|
||||
"sketch.halftonePattern",
|
||||
"sketch.reticulation",
|
||||
@@ -151,6 +152,8 @@ pub enum PixelFx {
|
||||
/// Sketch › Chalk & Charcoal: chalk highlights in light areas and charcoal in dark ones;
|
||||
/// `chalk` and `charcoal` (0..20) set how much, `pressure` (0..5) the stroke weight.
|
||||
ChalkCharcoal { chalk: f64, charcoal: f64, pressure: f64 },
|
||||
/// Sketch › Chrome: polished chrome relief with `detail` (0..10) and `smoothness` (0..10).
|
||||
Chrome { detail: f64, smoothness: f64 },
|
||||
/// Sketch › Graphic Pen: fine linear ink strokes along `dir`, each `length` (1..15) points long
|
||||
/// on white paper; `balance` (0..100) sets how much of the tone range becomes black ink.
|
||||
GraphicPen { length: f64, balance: f64, direction: StrokeDirection },
|
||||
@@ -339,6 +342,10 @@ pub(crate) fn parse(id: &str, p: &Value) -> Option<PixelFx> {
|
||||
charcoal: num(p, "charcoalArea", 6.0).clamp(0.0, 20.0),
|
||||
pressure: num(p, "strokePressure", 2.0).clamp(0.0, 5.0),
|
||||
},
|
||||
"sketch.chrome" => PixelFx::Chrome {
|
||||
detail: num(p, "detail", 4.0).clamp(0.0, 10.0),
|
||||
smoothness: num(p, "smoothness", 4.0).clamp(0.0, 10.0),
|
||||
},
|
||||
"sketch.graphicPen" => PixelFx::GraphicPen {
|
||||
length: num(p, "strokeLength", 5.0).clamp(1.0, 15.0),
|
||||
balance: num(p, "lightDarkBalance", 50.0).clamp(0.0, 100.0),
|
||||
@@ -432,7 +439,11 @@ impl PixelFx {
|
||||
// Content shifted outwards reaches past the bounds by as much as the shift.
|
||||
PixelFx::Glass { distortion, .. } => distort::glass_shift(distortion),
|
||||
PixelFx::OceanRipple { size, magnitude } => distort::ripple_shift(size, magnitude),
|
||||
PixelFx::UnsharpMask { .. } | PixelFx::ChalkCharcoal { .. } | PixelFx::HalftonePattern { .. } | PixelFx::Reticulation { .. } => 0.0,
|
||||
PixelFx::UnsharpMask { .. }
|
||||
| PixelFx::ChalkCharcoal { .. }
|
||||
| PixelFx::Chrome { .. }
|
||||
| PixelFx::HalftonePattern { .. }
|
||||
| PixelFx::Reticulation { .. } => 0.0,
|
||||
PixelFx::GlowingEdges { width, smoothness, .. } => width.max(smoothness * 3.0),
|
||||
PixelFx::FilmGrain { .. }
|
||||
| PixelFx::Cutout { .. }
|
||||
@@ -483,6 +494,7 @@ impl PixelFx {
|
||||
PixelFx::ColorHalftone { max_radius, .. } => Some(3.0 * std::f64::consts::SQRT_2 * max_radius),
|
||||
PixelFx::Crystallize { cell } => Some(1.5 * cell),
|
||||
PixelFx::ChalkCharcoal { .. } => Some(sketch::CHALK_CHARCOAL_REACH),
|
||||
PixelFx::Chrome { smoothness, .. } => Some(sketch::chrome_reach(smoothness)),
|
||||
PixelFx::FilmGrain { .. } | PixelFx::Mezzotint { .. } | PixelFx::HalftonePattern { .. } | PixelFx::Reticulation { .. } => Some(0.0),
|
||||
// Cutout's palette comes from the whole object.
|
||||
PixelFx::Cutout { .. } => None,
|
||||
@@ -540,6 +552,7 @@ impl PixelFx {
|
||||
PixelFx::OceanRipple { size, magnitude } => distort::ocean_ripple(px, w, h, space, size, magnitude),
|
||||
PixelFx::UnsharpMask { amount, radius, threshold } => sharpen::unsharp(px, w, h, amount, to_px(radius), threshold),
|
||||
PixelFx::ChalkCharcoal { chalk, charcoal, pressure } => sketch::chalk_charcoal(px, w, h, space, chalk, charcoal, pressure),
|
||||
PixelFx::Chrome { detail, smoothness } => sketch::chrome(px, w, h, space, detail, smoothness),
|
||||
PixelFx::GraphicPen { length, balance, direction } => brushstrokes::graphic_pen(px, w, h, space, length, balance, direction),
|
||||
PixelFx::HalftonePattern { size, contrast, kind } => sketch::halftone_pattern(px, w, h, space, size, contrast, kind),
|
||||
PixelFx::Reticulation { density, foreground, background } => sketch::reticulation(px, w, h, space, density, foreground, background),
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
//! Effect › Sketch (Chalk & Charcoal, Halftone Pattern and Reticulation; Graphic Pen lives in
|
||||
//! Effect › Sketch (Chalk & Charcoal, Chrome, Halftone Pattern and Reticulation; Graphic Pen lives in
|
||||
//! [`super::brushstrokes`]). Original filters built from public behaviour documentation:
|
||||
//! <https://helpx.adobe.com/illustrator/using/summary-effects.html> (Sketch effects redraw in
|
||||
//! black and white) and the public Photoshop Elements Artistic filters guide. Visual
|
||||
//! approximations, not proprietary reproductions.
|
||||
|
||||
use std::f64::consts::TAU;
|
||||
use std::f64::consts::{FRAC_1_SQRT_2, TAU};
|
||||
|
||||
use vectorcraft_geom::Vec2;
|
||||
|
||||
use super::edges::sobel;
|
||||
use super::pixelate::{Place, cell, rand, value_noise};
|
||||
use super::texture::{Tones, luma, premultiply, straight};
|
||||
use super::texture::{Tones, luma, premultiply, smooth_noise, straight};
|
||||
use super::{PixelSpace, blur_plane, pick};
|
||||
|
||||
const SALT_CHALK: u64 = 216;
|
||||
const SALT_CHROME: u64 = 0xC40_4D45;
|
||||
const EDGE_SOFT: f64 = 0.8;
|
||||
const TONE_SOFT: f64 = 1.0;
|
||||
/// Light from the top left (document axes, y down), as on polished relief surfaces.
|
||||
const CHROME_LIGHT: Vec2 = Vec2::new(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2);
|
||||
|
||||
/// How far (document units) Chalk & Charcoal samples softened tones and edges.
|
||||
pub(super) const CHALK_CHARCOAL_REACH: f64 = 3.0 * TONE_SOFT + 1.0 + 3.0 * EDGE_SOFT;
|
||||
@@ -138,6 +143,70 @@ pub(super) fn chalk_charcoal(px: &mut [[u8; 4]], w: usize, h: usize, space: &Pix
|
||||
});
|
||||
}
|
||||
|
||||
/// How far (document units) Chrome reaches for `smoothness` (0..10): 3 σ of the height-field blur
|
||||
/// plus the Sobel neighbours.
|
||||
pub(super) fn chrome_reach(smoothness: f64) -> f64 {
|
||||
let smoothness = smoothness.clamp(0.0, 10.0);
|
||||
3.0 * (0.4 + 0.55 * smoothness) + 1.0
|
||||
}
|
||||
|
||||
/// Chrome: the object as a polished chrome surface lit from the top left. `detail` (0..10) adds
|
||||
/// finer relief; `smoothness` (0..10) softens the height field before the highlights and shadows
|
||||
/// are drawn. Coverage is kept.
|
||||
pub(super) fn chrome(px: &mut [[u8; 4]], w: usize, h: usize, space: &PixelSpace, detail: f64, smoothness: f64) {
|
||||
if w == 0 || h == 0 {
|
||||
return;
|
||||
}
|
||||
let detail = detail.clamp(0.0, 10.0);
|
||||
let smoothness = smoothness.clamp(0.0, 10.0);
|
||||
let Some(place) = Place::new(space, w, h) else { return };
|
||||
let sigma = (0.4 + smoothness * 0.55) / place.px.max(1e-9);
|
||||
let cell = (14.0 - detail * 1.2).max(1.5);
|
||||
let bump = (detail / 10.0) * 0.4;
|
||||
let strength = 2.5 + detail * 0.35;
|
||||
let mut height: Vec<f32> = Vec::with_capacity(w * h);
|
||||
let mut alpha: Vec<f32> = Vec::with_capacity(w * h);
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
let p = px[y * w + x];
|
||||
let a = f32::from(p[3]) / 255.0;
|
||||
alpha.push(a);
|
||||
if a <= 0.0 {
|
||||
height.push(0.0);
|
||||
continue;
|
||||
}
|
||||
let q = place.at(x, y);
|
||||
let base = f64::from(luma(straight(p)));
|
||||
let n = if bump > 0.0 { bump * smooth_noise(SALT_CHROME, q, cell) } else { 0.0 };
|
||||
height.push((base + n).clamp(0.0, 1.0) as f32);
|
||||
}
|
||||
}
|
||||
blur_plane(&mut height, w, h, sigma);
|
||||
blur_plane(&mut alpha, w, h, sigma);
|
||||
let tone: Vec<f64> = height
|
||||
.iter()
|
||||
.zip(&alpha)
|
||||
.map(|(h, a)| if *a > 1e-4 { f64::from(*h / *a).clamp(0.0, 1.0) } else { 0.0 })
|
||||
.collect();
|
||||
let px_unit = place.px.max(1e-9);
|
||||
place.each(px, |x, y, p| {
|
||||
if p[3] == 0 {
|
||||
return;
|
||||
}
|
||||
let at = |dx: isize, dy: isize| {
|
||||
let (xx, yy) = ((x as isize + dx).clamp(0, w as isize - 1) as usize, (y as isize + dy).clamp(0, h as isize - 1) as usize);
|
||||
tone[yy * w + xx]
|
||||
};
|
||||
let h0 = tone[y * w + x];
|
||||
let gx = 0.5 * (at(1, 0) - at(-1, 0)) / px_unit;
|
||||
let gy = 0.5 * (at(0, 1) - at(0, -1)) / px_unit;
|
||||
let face = -(gx * CHROME_LIGHT.x + gy * CHROME_LIGHT.y);
|
||||
let spec = (face * strength + (h0 - 0.5) * 1.5).tanh();
|
||||
let grey = (0.5 + 0.5 * spec).clamp(0.0, 1.0);
|
||||
*p = premultiply([grey as f32; 3], f32::from(p[3]) / 255.0);
|
||||
});
|
||||
}
|
||||
|
||||
/// Reticulation: the object redrawn in black ink on white paper through a high-contrast film-grain
|
||||
/// screen. `density` (0..50) sets how fine the grain is; `foreground` and `background` (0..50 each)
|
||||
/// pull shadows toward black and highlights toward white before the grain decides ink. Coverage is
|
||||
|
||||
@@ -2187,6 +2187,12 @@ fn halftone_draws_in_black_and_white_only() {
|
||||
}
|
||||
}
|
||||
|
||||
fn chrome(p: serde_json::Value, src: &[u8], w: usize, h: usize, s: &PixelSpace) -> Vec<u8> {
|
||||
let mut out = src.to_vec();
|
||||
fx("sketch.chrome", p).apply(&mut out, w, h, s);
|
||||
out
|
||||
}
|
||||
|
||||
/// Reticulation of `p` over `w` × `h` pixels of `src`, laid out by `s`.
|
||||
fn reticulation(p: serde_json::Value, src: &[u8], w: usize, h: usize, s: &PixelSpace) -> Vec<u8> {
|
||||
let mut out = src.to_vec();
|
||||
@@ -2194,6 +2200,39 @@ fn reticulation(p: serde_json::Value, src: &[u8], w: usize, h: usize, s: &PixelS
|
||||
out
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chrome_params_take_defaults_and_stay_in_range() {
|
||||
assert_eq!(fx("sketch.chrome", json!({})), PixelFx::Chrome { detail: 4.0, smoothness: 4.0 });
|
||||
assert_eq!(fx("sketch.chrome", json!({"detail": -1, "smoothness": 99})), PixelFx::Chrome { detail: 0.0, smoothness: 10.0 });
|
||||
assert_eq!(fx("sketch.chrome", json!({"detail": "x"})), fx("sketch.chrome", json!({})));
|
||||
let doc = crate::effect_info("sketch.chrome").unwrap().params;
|
||||
assert!(doc.contains("detail") && doc.contains("smoothness"), "{doc}");
|
||||
let f = fx("sketch.chrome", json!({}));
|
||||
assert_eq!(f.outset(Rect::new(0.0, 0.0, 100.0, 50.0)), 0.0);
|
||||
assert_eq!(f.reach(), Some(sketch::chrome_reach(4.0)));
|
||||
assert_eq!(fx("sketch.chrome", json!({"smoothness": 10})).reach(), Some(sketch::chrome_reach(10.0)));
|
||||
}
|
||||
|
||||
/// Chrome redraws in greyscale, keeps coverage, and more detail changes the pattern.
|
||||
#[test]
|
||||
fn chrome_draws_polished_greyscale_relief() {
|
||||
let (w, h) = (64, 64);
|
||||
let src = image(w, h, |x, y| {
|
||||
let t = ((x + y) * 4) as u8;
|
||||
[t, t / 2, 255 - t, 255]
|
||||
});
|
||||
let soft = chrome(json!({"detail": 0, "smoothness": 8}), &src, w, h, &space(w, h));
|
||||
let sharp = chrome(json!({"detail": 10, "smoothness": 0}), &src, w, h, &space(w, h));
|
||||
assert_ne!(soft, sharp);
|
||||
for out in [&soft, &sharp] {
|
||||
for (p, q) in out.as_chunks::<4>().0.iter().zip(src.as_chunks::<4>().0) {
|
||||
assert_eq!(p[3], q[3], "coverage kept");
|
||||
assert!(p[0] == p[1] && p[1] == p[2], "greyscale {p:?}");
|
||||
}
|
||||
assert!(share(out, |p| p[0] > 10 && p[0] < 245) > 0.05, "midtones");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reticulation_params_take_defaults_and_stay_in_range() {
|
||||
assert_eq!(fx("sketch.reticulation", json!({})), PixelFx::Reticulation { density: 12.0, foreground: 6.0, background: 6.0 });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! The Photoshop-style raster effects (Effect › Artistic, Blur › Radial Blur and Smart Blur, Brush Strokes,
|
||||
//! Distort › Diffuse Glow, Glass and Ocean Ripple, Pixelate › Color Halftone, Crystallize, Mezzotint and Pointillize, Sharpen › Unsharp Mask, Sketch › Halftone Pattern, Texture ›
|
||||
//! Distort › Diffuse Glow, Glass and Ocean Ripple, Pixelate › Color Halftone, Crystallize, Mezzotint and Pointillize, Sharpen › Unsharp Mask, Sketch › Chalk & Charcoal, Chrome, Graphic Pen, Halftone Pattern and Reticulation, Texture ›
|
||||
//! Craquelure, Grain, Mosaic Tiles, Patchwork, Stained Glass and Texturizer, Video): applied and
|
||||
//! edited as commands, listed in the catalogue, drawn on the canvas, and written to PDF and SVG
|
||||
//! as images of the effected object.
|
||||
@@ -49,8 +49,13 @@ const PIXELATE: [(&str, &str); 4] =
|
||||
[("pixelate.colorHalftone", "{}"), ("pixelate.crystallize", "{}"), ("pixelate.mezzotint", "{}"), ("pixelate.pointillize", "{}")];
|
||||
|
||||
/// Effect › Sketch, at their defaults.
|
||||
const SKETCH: [(&str, &str); 4] =
|
||||
[("sketch.chalkCharcoal", "{}"), ("sketch.graphicPen", "{}"), ("sketch.halftonePattern", "{}"), ("sketch.reticulation", "{}")];
|
||||
const SKETCH: [(&str, &str); 5] = [
|
||||
("sketch.chalkCharcoal", "{}"),
|
||||
("sketch.chrome", "{}"),
|
||||
("sketch.graphicPen", "{}"),
|
||||
("sketch.halftonePattern", "{}"),
|
||||
("sketch.reticulation", "{}"),
|
||||
];
|
||||
|
||||
/// Effect › Texture, at their defaults.
|
||||
const TEXTURE: [(&str, &str); 6] = [
|
||||
|
||||
@@ -334,6 +334,7 @@ fn structured_junk() {
|
||||
("effect.apply", json!({"effect": "artistic.cutout", "params": {"levels": 1e308, "edgeSimplicity": "NaN", "edgeFidelity": -1e308}})),
|
||||
("effect.apply", json!({"effect": "artistic.neonGlow", "params": {"glowSize": -1e308, "glowBrightness": "1e999", "color": [1]}})),
|
||||
("effect.apply", json!({"effect": "artistic.posterEdges", "params": {"edgeThickness": 1e308, "edgeIntensity": [], "posterization": -7}})),
|
||||
("effect.apply", json!({"effect": "sketch.chrome", "params": {"detail": -1e308, "smoothness": "NaN"}})),
|
||||
("effect.apply", json!({"effect": "sketch.graphicPen", "params": {"strokeLength": -1e308, "lightDarkBalance": "NaN", "strokeDirection": 3}})),
|
||||
("effect.apply", json!({"effect": "sketch.halftonePattern", "params": {"size": -1e308, "contrast": "NaN", "patternType": 3}})),
|
||||
("effect.apply", json!({"effect": "sketch.reticulation", "params": {"density": "NaN", "foregroundLevel": -1e308, "backgroundLevel": 1e308}})),
|
||||
|
||||
@@ -171,23 +171,24 @@ fn effect_menu_lists_the_texture_filters_between_sharpen_and_video() {
|
||||
assert!(at("Sharpen") < at("Texture") && at("Texture") < at("Video"));
|
||||
}
|
||||
|
||||
/// Effect › Sketch, between Sharpen and the raster Stylize: Chalk & Charcoal and Halftone Pattern open their dialogs.
|
||||
/// Effect › Sketch, between Sharpen and the raster Stylize: the raster sketch filters open their dialogs.
|
||||
#[test]
|
||||
fn effect_menu_lists_the_sketch_filters_between_sharpen_and_stylize() {
|
||||
let app = app();
|
||||
let entries = menus::menu_entries(&app);
|
||||
let sketch: Vec<_> = entries.iter().filter(|e| e.path == ["Effect", "Sketch"]).collect();
|
||||
let labels: Vec<&str> = sketch.iter().map(|e| e.label.as_str()).collect();
|
||||
assert_eq!(labels, ["Chalk & Charcoal…", "Graphic Pen…", "Halftone Pattern…", "Reticulation…"]);
|
||||
assert_eq!(sketch[0].command.as_deref(), Some("effect.dialog"));
|
||||
assert_eq!(labels, ["Chalk & Charcoal…", "Chrome…", "Graphic Pen…", "Halftone Pattern…", "Reticulation…"]);
|
||||
assert!(sketch.iter().all(|e| e.command.as_deref() == Some("effect.dialog")));
|
||||
assert_eq!(sketch[0].params, json!({"effect": "sketch.chalkCharcoal"}));
|
||||
assert_eq!(sketch[1].params, json!({"effect": "sketch.graphicPen"}));
|
||||
assert_eq!(sketch[2].params, json!({"effect": "sketch.halftonePattern"}));
|
||||
assert_eq!(sketch[3].params, json!({"effect": "sketch.reticulation"}));
|
||||
assert_eq!(sketch[1].params, json!({"effect": "sketch.chrome"}));
|
||||
assert_eq!(sketch[2].params, json!({"effect": "sketch.graphicPen"}));
|
||||
assert_eq!(sketch[3].params, json!({"effect": "sketch.halftonePattern"}));
|
||||
assert_eq!(sketch[4].params, json!({"effect": "sketch.reticulation"}));
|
||||
let at = |label: &str| entries.iter().position(|e| e.path[0] == "Effect" && e.label == label).unwrap();
|
||||
assert!(at("Unsharp Mask…") < at("Chalk & Charcoal…") && at("Chalk & Charcoal…") < at("Graphic Pen…"));
|
||||
assert!(at("Graphic Pen…") < at("Halftone Pattern…") && at("Halftone Pattern…") < at("Reticulation…"));
|
||||
assert!(at("Reticulation…") < at("Glowing Edges…"));
|
||||
assert!(at("Unsharp Mask…") < at("Chalk & Charcoal…") && at("Chalk & Charcoal…") < at("Chrome…"));
|
||||
assert!(at("Chrome…") < at("Graphic Pen…") && at("Graphic Pen…") < at("Halftone Pattern…"));
|
||||
assert!(at("Halftone Pattern…") < at("Reticulation…") && at("Reticulation…") < at("Glowing Edges…"));
|
||||
}
|
||||
|
||||
/// Effect › Stylize under Raster Effects, between Sharpen and Texture: Glowing Edges opens its
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Effects parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-19 · **Change:** minor (Sketch › Chalk & Charcoal, Graphic Pen and Reticulation and Artistic › Plastic Wrap, #2972, #2967, #2976, #3034: 36/57 Photoshop effects; 3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; Zig Zag's Smooth points make a continuous wave, #2343; G2: Effect › Sketch › Halftone Pattern; Photoshop filters 30/57, 27 remaining (22–38 h); Round Corners also rounds corners where a curve meets a line or another curve, with Live Corners' cut; straight corners unchanged; score unchanged; Revolve: automatic smooth shading and default-on Cap with continuous rims; wide shadows and glows at high zoom, #1706; stacked Transform effects, #1705; 3D on nonzero compound paths, #1724; pattern tiles redraw, #1723; scores unchanged; Roughen's Smooth turn-backs, #1643; Scribble per region, #1648; score unchanged; Warp Arc bends in the object's proportions, #1578; Zig Zag ridges evenly spaced, #1558; effect parameters checked, #1592; score unchanged; raster effects after a pattern, radial or freeform gradient fill export at their place and size, #1536; score unchanged; 3D and Materials on outlined type and other shapes of many small curves, #1481; Twist 0° to 360°, #1524; twisted walls keep their faces, #1525; score unchanged; 3D and Materials: Inflate takes Twist, Taper and Cap; score unchanged; 3D and Materials: Perspective as the documented 0°–160° angle; 3D and Materials: the documented light settings) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-19 · **Change:** minor (G2: Sketch › Chrome; Sketch › Chalk & Charcoal, Graphic Pen and Reticulation and Artistic › Plastic Wrap, #2972, #2967, #2976, #3034: 37/57 Photoshop effects; 3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; Zig Zag's Smooth points make a continuous wave, #2343; G2: Effect › Sketch › Halftone Pattern; Photoshop filters 30/57, 27 remaining (22–38 h); Round Corners also rounds corners where a curve meets a line or another curve, with Live Corners' cut; straight corners unchanged; score unchanged; Revolve: automatic smooth shading and default-on Cap with continuous rims; wide shadows and glows at high zoom, #1706; stacked Transform effects, #1705; 3D on nonzero compound paths, #1724; pattern tiles redraw, #1723; scores unchanged; Roughen's Smooth turn-backs, #1643; Scribble per region, #1648; score unchanged; Warp Arc bends in the object's proportions, #1578; Zig Zag ridges evenly spaced, #1558; effect parameters checked, #1592; score unchanged; raster effects after a pattern, radial or freeform gradient fill export at their place and size, #1536; score unchanged; 3D and Materials on outlined type and other shapes of many small curves, #1481; Twist 0° to 360°, #1524; twisted walls keep their faces, #1525; score unchanged; 3D and Materials: Inflate takes Twist, Taper and Cap; score unchanged; 3D and Materials: Perspective as the documented 0°–160° angle; 3D and Materials: the documented light settings) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
Every entry of Illustrator's Effect menu, by its public feature name, against VectorCraft's effects catalogue
|
||||
(`effect_catalog()` in `crates/effects/src/lib.rs`) and the Effect menu built from it
|
||||
@@ -44,7 +44,7 @@ its resolution, PDF export warns once per object with the requested and the lowe
|
||||
| Distort | Diffuse Glow, Glass, Ocean Ripple | 3/3 | — |
|
||||
| Pixelate | Color Halftone, Crystallize, Mezzotint, Pointillize | 4/4 | — |
|
||||
| Sharpen | Unsharp Mask | 1/1 | — |
|
||||
| Sketch | Bas Relief, Chalk & Charcoal, Charcoal, Chrome, Conté Crayon, Graphic Pen, Halftone Pattern, Note Paper, Photocopy, Plaster, Reticulation, Stamp, Torn Edges, Water Paper | 4/14 (approximate Chalk & Charcoal, Graphic Pen, Halftone Pattern, Reticulation; defaults unverified) | Bas Relief, Charcoal, Chrome, Conté Crayon, Note Paper, Photocopy, Plaster, Stamp, Torn Edges, Water Paper |
|
||||
| Sketch | Bas Relief, Chalk & Charcoal, Charcoal, Chrome, Conté Crayon, Graphic Pen, Halftone Pattern, Note Paper, Photocopy, Plaster, Reticulation, Stamp, Torn Edges, Water Paper | 5/14 (approximate Chalk & Charcoal, Chrome, Graphic Pen, Halftone Pattern, Reticulation; Chalk & Charcoal and Graphic Pen defaults unverified) | Bas Relief, Charcoal, Conté Crayon, Note Paper, Photocopy, Plaster, Stamp, Torn Edges, Water Paper |
|
||||
| Stylize | Glowing Edges | 1/1 | — |
|
||||
| Texture | Craquelure, Grain, Mosaic Tiles, Patchwork, Stained Glass, Texturizer | 6/6 | Texturizer's Load Texture |
|
||||
| Video | De-Interlace, NTSC Colors | 2/2 | — |
|
||||
|
||||
+2
-2
@@ -226,8 +226,8 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall
|
||||
- **Missing:** 23 of 57 filters (the remaining 11 Artistic filters and 12 of Sketch's 14), the Effect Gallery dialog, Load
|
||||
Texture for Texturizer. Measured in
|
||||
[effects-parity.md](effects-parity.md).
|
||||
- **Done:** the pixel-filter pipeline and 34 filters (Artistic: Cutout, Film Grain, Neon Glow, Poster Edges, Blur, Brush Strokes, Distort, Pixelate, Sharpen, Sketch:
|
||||
Graphic Pen and Halftone Pattern, Stylize, Texture, Video), Brush Strokes on 2026-10-11, Film Grain on 2026-10-12, Halftone Pattern on 2026-10-16, Graphic Pen on 2026-10-18, Cutout, Neon Glow and Poster Edges on 2026-10-16, and Smart Blur's Edge Only and Overlay Edge
|
||||
- **Done:** the pixel-filter pipeline and 35 filters (Artistic: Cutout, Film Grain, Neon Glow, Poster Edges, Blur, Brush Strokes, Distort, Pixelate, Sharpen, Sketch:
|
||||
Chalk & Charcoal, Chrome, Graphic Pen and Halftone Pattern, Stylize, Texture, Video), Brush Strokes on 2026-10-11, Film Grain on 2026-10-12, Halftone Pattern on 2026-10-16, Graphic Pen on 2026-10-18, Chrome on 2026-10-19, Cutout, Neon Glow and Poster Edges on 2026-10-16, and Smart Blur's Edge Only and Overlay Edge
|
||||
modes.
|
||||
- **Estimate:** 21–35 h (measured 0.7–1.2 h per filter); parallelizes well.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user