Merge pull request #4299 from dylanmtaylor/sonar/enforce-clippy-standards

Enforce Rust coding standards via Clippy
This commit is contained in:
Dylan M. Taylor
2026-10-28 18:41:55 -04:00
committed by GitHub
441 changed files with 6854 additions and 4776 deletions
+1
View File
@@ -35,6 +35,7 @@ Formerly **DrawCraft** (renamed 2026-10-01): old `.drawcraft` files and `"format
- **Never crash: no panics in shipped code.** A crash loses the user's unsaved work. Files, pasted data, MCP and control messages, and command params are all untrusted input.
- Fallible work returns `Result` (or `Option`); callers handle it or propagate it with `?`. Failures reach the user as an error message (`EngineError` for commands, the crate's own error type or `String` below the engine), never as an abort.
- Workspace clippy lints ban these outside tests, so `cargo xtask ci` fails on them: `unwrap()`, `expect()`, `panic!`, `unreachable!`, `todo!`, `unimplemented!`. Use instead: `?`, `.ok_or(…)?`, `let … else { return … }`, `if let`, `unwrap_or` / `unwrap_or_default` / `unwrap_or_else`, `.first()` / `.last()` / `.split_last()`, and a real error for a match arm you believe is impossible.
- New workspace crates must include `[lints] workspace = true` in `Cargo.toml` to inherit `[workspace.lints]`. Production crate roots must declare `#![forbid(unsafe_code)]` before modules or other crate items; crate-level `//!` documentation may precede it.
- Don't panic implicitly either: use `.get(i)` instead of `v[i]` or `&s[a..b]` when the index comes from data. Don't divide integers by something that can be zero, cap sizes and counts read from input (allocations, images, loops), and check that floats are finite before casting them or looping on them.
- Don't silence errors just to satisfy the lint: use `let _ =` or `.ok()` only where ignoring the failure is the right behaviour, with a comment saying why.
- Tests may unwrap and panic (`#[test]`, `#[cfg(test)]`, `tests/`, `testkit`). Integration-test files start with an `#![allow(…)]` for that.
+3
View File
@@ -116,6 +116,9 @@ webbrowser = "1"
unsafe_code = "deny"
[workspace.lints.clippy]
cognitive_complexity = "deny"
too_many_arguments = "deny"
wildcard_imports = "deny"
dbg_macro = "warn"
# VectorCraft never crashes: shipped code returns `Result`/`Option` instead of panicking (see
# AGENTS.md › Robustness). Tests may unwrap (clippy.toml).
+110 -74
View File
@@ -447,113 +447,158 @@ fn mcp_protocol_over_stdio() {
let mut stdin = child.stdin.take().unwrap();
let mut lines = BufReader::new(child.stdout.take().unwrap()).lines();
let mut notes = vec![];
fn send(stdin: &mut std::process::ChildStdin, v: Value) {
writeln!(stdin, "{v}").unwrap();
exercise_mcp_core(&mut stdin, &mut lines, &mut notes);
exercise_mcp_resource_errors(&mut stdin, &mut lines, &mut notes);
exercise_mcp_document_resources(&mut stdin, &mut lines, &mut notes);
exercise_mcp_logging(&mut stdin, &mut lines, &mut notes);
exercise_mcp_parse_error(&mut stdin, &mut lines, &mut notes);
drop(stdin);
for line in lines {
let v: Value = serde_json::from_str(&line.unwrap()).unwrap();
assert_eq!(v["jsonrpc"], "2.0", "{v}");
notes.push(v);
}
// Next reply with this id; notifications (no id) are stashed for later.
fn recv(lines: &mut std::io::Lines<BufReader<std::process::ChildStdout>>, notes: &mut Vec<Value>, id: u64) -> Value {
loop {
let line = lines.next().unwrap().unwrap();
let v: Value = serde_json::from_str(&line).unwrap();
assert_eq!(v["jsonrpc"], "2.0", "{v}");
if v.get("id").and_then(|i| i.as_u64()) == Some(id) {
return v;
}
notes.push(v);
let out = child.wait_with_output().unwrap();
assert!(out.status.success());
assert!(!String::from_utf8_lossy(&out.stderr).contains("panicked"));
let messages = notes.iter().filter(|n| n["method"] == "notifications/message").count();
assert!(messages > 0, "no log records after logging/setLevel: {notes:?}");
}
fn mcp_send(stdin: &mut std::process::ChildStdin, v: Value) {
writeln!(stdin, "{v}").unwrap();
}
fn mcp_recv(lines: &mut std::io::Lines<std::io::BufReader<std::process::ChildStdout>>, notes: &mut Vec<Value>, id: u64) -> Value {
loop {
let line = lines.next().unwrap().unwrap();
let v: Value = serde_json::from_str(&line).unwrap();
assert_eq!(v["jsonrpc"], "2.0", "{v}");
if v.get("id").and_then(|i| i.as_u64()) == Some(id) {
return v;
}
notes.push(v);
}
}
send(
&mut stdin,
fn exercise_mcp_core(
stdin: &mut std::process::ChildStdin,
lines: &mut std::io::Lines<std::io::BufReader<std::process::ChildStdout>>,
notes: &mut Vec<Value>,
) {
mcp_send(
stdin,
json!({"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"test","version":"0"}}}),
);
let r = recv(&mut lines, &mut notes, 1);
let r = mcp_recv(lines, notes, 1);
assert_eq!(r["result"]["serverInfo"]["name"], "vectorcraft");
assert!(r["result"]["capabilities"]["prompts"].is_object(), "{r}");
send(&mut stdin, json!({"jsonrpc":"2.0","method":"notifications/initialized"}));
mcp_send(stdin, json!({"jsonrpc":"2.0","method":"notifications/initialized"}));
send(&mut stdin, json!({"jsonrpc":"2.0","id":2,"method":"prompts/list","params":{}}));
let r = recv(&mut lines, &mut notes, 2);
mcp_send(stdin, json!({"jsonrpc":"2.0","id":2,"method":"prompts/list","params":{}}));
let r = mcp_recv(lines, notes, 2);
assert!(r["result"]["prompts"].as_array().is_some_and(|p| !p.is_empty()), "{r}");
send(&mut stdin, json!({"jsonrpc":"2.0","id":3,"method":"prompts/get","params":{"name":"poster","arguments":{"brief":"a jazz festival"}}}));
let r = recv(&mut lines, &mut notes, 3);
mcp_send(stdin, json!({"jsonrpc":"2.0","id":3,"method":"prompts/get","params":{"name":"poster","arguments":{"brief":"a jazz festival"}}}));
let r = mcp_recv(lines, notes, 3);
assert!(r["result"]["messages"][0]["content"]["text"].as_str().is_some_and(|t| t.contains("a jazz festival")), "{r}");
send(
&mut stdin,
mcp_send(
stdin,
json!({"jsonrpc":"2.0","id":4,"method":"completion/complete","params":{"ref":{"type":"ref/prompt","name":"export-set"},"argument":{"name":"formats","value":"sv"}}}),
);
let r = recv(&mut lines, &mut notes, 4);
let r = mcp_recv(lines, notes, 4);
assert!(r["result"]["completion"]["values"].as_array().is_some_and(|v| v.contains(&json!("svg"))), "{r}");
send(&mut stdin, json!({"jsonrpc":"2.0","id":5,"method":"resources/list","params":{}}));
assert!(recv(&mut lines, &mut notes, 5)["result"]["resources"].as_array().is_some_and(|v| v.len() == 3));
send(&mut stdin, json!({"jsonrpc":"2.0","id":6,"method":"resources/templates/list","params":{}}));
assert!(recv(&mut lines, &mut notes, 6)["result"]["resourceTemplates"].as_array().is_some_and(|v| v.len() >= 4));
mcp_send(stdin, json!({"jsonrpc":"2.0","id":5,"method":"resources/list","params":{}}));
assert!(mcp_recv(lines, notes, 5)["result"]["resources"].as_array().is_some_and(|v| v.len() == 3));
mcp_send(stdin, json!({"jsonrpc":"2.0","id":6,"method":"resources/templates/list","params":{}}));
assert!(mcp_recv(lines, notes, 6)["result"]["resourceTemplates"].as_array().is_some_and(|v| v.len() >= 4));
send(
&mut stdin,
mcp_send(
stdin,
json!({"jsonrpc":"2.0","id":7,"method":"tools/call","params":{"name":"draw_shape","arguments":{"shape":"rectangle","x":10,"y":10,"width":60,"height":40}}}),
);
let r = recv(&mut lines, &mut notes, 7);
let r = mcp_recv(lines, notes, 7);
assert_eq!(r["result"]["isError"], false, "{r}");
let id: i64 = serde_json::from_str::<Value>(r["result"]["content"][0]["text"].as_str().unwrap()).unwrap()["id"].as_i64().unwrap();
send(&mut stdin, json!({"jsonrpc":"2.0","id":8,"method":"resources/read","params":{"uri": format!("vectorcraft://object/{id}")}}));
let r = recv(&mut lines, &mut notes, 8);
mcp_send(stdin, json!({"jsonrpc":"2.0","id":8,"method":"resources/read","params":{"uri": format!("vectorcraft://object/{id}")}}));
let r = mcp_recv(lines, notes, 8);
let body: Value = serde_json::from_str(r["result"]["contents"][0]["text"].as_str().unwrap()).unwrap();
assert_eq!(body["id"].as_i64(), Some(id), "{body}");
}
fn exercise_mcp_resource_errors(
stdin: &mut std::process::ChildStdin,
lines: &mut std::io::Lines<std::io::BufReader<std::process::ChildStdout>>,
notes: &mut Vec<Value>,
) {
// Unknown resources (-32002) and bad template values (-32602) stay apart.
send(&mut stdin, json!({"jsonrpc":"2.0","id":9,"method":"resources/read","params":{"uri":"vectorcraft://nope" }}));
assert_eq!(recv(&mut lines, &mut notes, 9)["error"]["code"], -32002);
send(&mut stdin, json!({"jsonrpc":"2.0","id":10,"method":"resources/read","params":{"uri":"vectorcraft://object/"}}));
assert_eq!(recv(&mut lines, &mut notes, 10)["error"]["code"], -32602);
mcp_send(stdin, json!({"jsonrpc":"2.0","id":9,"method":"resources/read","params":{"uri":"vectorcraft://nope" }}));
assert_eq!(mcp_recv(lines, notes, 9)["error"]["code"], -32002);
mcp_send(stdin, json!({"jsonrpc":"2.0","id":10,"method":"resources/read","params":{"uri":"vectorcraft://object/"}}));
assert_eq!(mcp_recv(lines, notes, 10)["error"]["code"], -32602);
}
fn exercise_mcp_document_resources(
stdin: &mut std::process::ChildStdin,
lines: &mut std::io::Lines<std::io::BufReader<std::process::ChildStdout>>,
notes: &mut Vec<Value>,
) {
// Large containers slice through run_command document.node: a truncated level reports
// childCount, and a bad limit is a tool error, not a crash or a full read.
send(
&mut stdin,
mcp_send(
stdin,
json!({"jsonrpc":"2.0","id":20,"method":"tools/call","params":{"name":"draw_shape","arguments":{"shape":"ellipse","x":90,"y":10,"width":30,"height":30}}}),
);
assert_eq!(recv(&mut lines, &mut notes, 20)["result"]["isError"], false);
let run = |id: u64, command: &str, params: Value| json!({"jsonrpc":"2.0","id":id,"method":"tools/call","params":{"name":"run_command","arguments":{"command":command,"params":params}}});
send(&mut stdin, run(21, "select.all", json!({})));
recv(&mut lines, &mut notes, 21);
send(&mut stdin, run(22, "object.group", json!({})));
let r = recv(&mut lines, &mut notes, 22);
assert_eq!(mcp_recv(lines, notes, 20)["result"]["isError"], false);
mcp_send(stdin, run_command(21, "select.all", json!({})));
mcp_recv(lines, notes, 21);
mcp_send(stdin, run_command(22, "object.group", json!({})));
let r = mcp_recv(lines, notes, 22);
let group = serde_json::from_str::<Value>(r["result"]["content"][0]["text"].as_str().unwrap()).unwrap()["id"].clone();
send(&mut stdin, run(23, "document.node", json!({"id": group, "summary": true, "childLimit": 1})));
let r = recv(&mut lines, &mut notes, 23);
mcp_send(stdin, run_command(23, "document.node", json!({"id": group, "summary": true, "childLimit": 1})));
let r = mcp_recv(lines, notes, 23);
let sliced: Value = serde_json::from_str(r["result"]["content"][0]["text"].as_str().unwrap()).unwrap();
assert_eq!(sliced["children"].as_array().map(Vec::len), Some(1), "{sliced}");
assert_eq!(sliced["childCount"], 2, "{sliced}");
send(&mut stdin, run(24, "document.node", json!({"id": group, "summary": true, "depth": -1})));
assert_eq!(recv(&mut lines, &mut notes, 24)["result"]["isError"], true);
mcp_send(stdin, run_command(24, "document.node", json!({"id": group, "summary": true, "depth": -1})));
assert_eq!(mcp_recv(lines, notes, 24)["result"]["isError"], true);
// Skeleton then drill: a depth-0 inspect is the layer skeleton with counts, and
// document.find locates nodes without dumping the tree.
send(&mut stdin, run(25, "document.inspect", json!({"depth": 0})));
let r = recv(&mut lines, &mut notes, 25);
mcp_send(stdin, run_command(25, "document.inspect", json!({"depth": 0})));
let r = mcp_recv(lines, notes, 25);
let skel: Value = serde_json::from_str(r["result"]["content"][0]["text"].as_str().unwrap()).unwrap();
assert!(skel["layers"][0].get("children").is_none(), "{skel}");
assert!(skel["artboards"].as_array().is_some_and(|a| !a.is_empty()), "{skel}");
send(&mut stdin, run(26, "document.find", json!({"kind": "ellipse"})));
let r = recv(&mut lines, &mut notes, 26);
mcp_send(stdin, run_command(26, "document.find", json!({"kind": "ellipse"})));
let r = mcp_recv(lines, notes, 26);
let found: Value = serde_json::from_str(r["result"]["content"][0]["text"].as_str().unwrap()).unwrap();
assert_eq!(found["total"], 1, "{found}");
assert!(found["matches"][0]["path"].as_array().is_some_and(|p| !p.is_empty()), "{found}");
send(&mut stdin, run(27, "document.find", json!({})));
assert_eq!(recv(&mut lines, &mut notes, 27)["result"]["isError"], true);
mcp_send(stdin, run_command(27, "document.find", json!({})));
assert_eq!(mcp_recv(lines, notes, 27)["result"]["isError"], true);
}
fn exercise_mcp_logging(
stdin: &mut std::process::ChildStdin,
lines: &mut std::io::Lines<std::io::BufReader<std::process::ChildStdout>>,
notes: &mut Vec<Value>,
) {
// The binary installed its logger: records arrive as notifications/message, and only
// after the client asked for them.
send(&mut stdin, json!({"jsonrpc":"2.0","id":11,"method":"logging/setLevel","params":{"level":"debug"}}));
assert!(recv(&mut lines, &mut notes, 11).get("error").is_none());
send(&mut stdin, json!({"jsonrpc":"2.0","id":12,"method":"tools/list","params":{}}));
assert!(recv(&mut lines, &mut notes, 12)["result"]["tools"].as_array().is_some_and(|v| !v.is_empty()));
mcp_send(stdin, json!({"jsonrpc":"2.0","id":11,"method":"logging/setLevel","params":{"level":"debug"}}));
assert!(mcp_recv(lines, notes, 11).get("error").is_none());
mcp_send(stdin, json!({"jsonrpc":"2.0","id":12,"method":"tools/list","params":{}}));
assert!(mcp_recv(lines, notes, 12)["result"]["tools"].as_array().is_some_and(|v| !v.is_empty()));
}
fn exercise_mcp_parse_error(
stdin: &mut std::process::ChildStdin,
lines: &mut std::io::Lines<std::io::BufReader<std::process::ChildStdout>>,
notes: &mut Vec<Value>,
) {
// Malformed input is a parse error, and the server keeps serving afterwards.
stdin.write_all(b"{not json\n").unwrap();
let parse_err: Value = loop {
@@ -566,21 +611,12 @@ fn mcp_protocol_over_stdio() {
};
assert_eq!(parse_err["error"]["code"], -32700);
assert!(parse_err["id"].is_null(), "{parse_err}");
send(&mut stdin, json!({"jsonrpc":"2.0","id":13,"method":"ping","params":{}}));
assert!(recv(&mut lines, &mut notes, 13).get("error").is_none());
drop(stdin);
// Drain whatever the debug window queued; every line off the protocol stream parses.
for line in lines.by_ref() {
let line = line.unwrap();
let v: Value = serde_json::from_str(&line).unwrap();
assert_eq!(v["jsonrpc"], "2.0", "{v}");
notes.push(v);
}
let out = child.wait_with_output().unwrap();
assert!(out.status.success());
assert!(!String::from_utf8_lossy(&out.stderr).contains("panicked"));
let messages = notes.iter().filter(|n| n["method"] == "notifications/message").count();
assert!(messages > 0, "no log records after logging/setLevel: {notes:?}");
mcp_send(stdin, json!({"jsonrpc":"2.0","id":13,"method":"ping","params":{}}));
assert!(mcp_recv(lines, notes, 13).get("error").is_none());
}
fn run_command(id: u64, command: &str, params: Value) -> Value {
json!({"jsonrpc":"2.0","id":id,"method":"tools/call","params":{"name":"run_command","arguments":{"command":command,"params":params}}})
}
/// `vectorcraft-cli commands | head -1` panicked with "failed printing to
+3 -1
View File
@@ -1,5 +1,7 @@
use super::prefs::save_prefs;
use super::*;
#[cfg(all(windows, not(target_vendor = "win7")))]
use super::windows_stylus;
use super::{App, VectorcraftApp, file_access, window};
/// The parts of [`App::raw_input_hook`] that every window needs, for the OS windows of floating
/// panels: a click or Escape that closes the macOS menu bar's open menu, and the pen barrel
+4 -2
View File
@@ -164,7 +164,9 @@ mod win {
use clipboard_win::{formats, options::NoClear, raw};
use super::*;
use super::{
BITMAP, Cursor, Flavour, PNG, SVG, SystemClipboard, TEXT, copied_file, decode_png, dib_image, png_flavour, text_of, windows_formats,
};
/// The device-independent bitmaps, the one that carries alpha first. Each is there whenever the
/// other or a `CF_BITMAP` is: the system converts between them.
@@ -309,7 +311,7 @@ mod win {
mod portable {
use std::borrow::Cow;
use super::*;
use super::{BITMAP, Flavour, PNG, SystemClipboard, TEXT, copied_file, decode_png, png_flavour, text_of};
/// What our last write put on the clipboard.
enum Ours {
+7 -1
View File
@@ -1,4 +1,10 @@
use super::*;
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))]
use super::portal_save;
#[cfg(target_os = "linux")]
use super::tablet;
#[cfg(all(windows, not(target_vendor = "win7")))]
use super::windows_stylus;
use super::{ClipboardProbeFactory, FilePick, PickRequest, Services, clipboard, cursor_theme, fileio, printing};
/// The window file dialogs belong to.
pub(crate) type Parent = Option<std::sync::Arc<winit::window::Window>>;
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{VectorcraftApp, fileio, prefs_dir};
/// Where UI preferences live: ~/Library/Application Support/VectorCraft (macOS),
/// %APPDATA%\VectorCraft (Windows), $XDG_CONFIG_HOME or ~/.config/vectorcraft (Linux).
+1 -1
View File
@@ -38,7 +38,7 @@ impl PrintService for SystemPrint {
#[cfg(not(all(windows, any(feature = "windows7", target_vendor = "win7"))))]
mod system {
use super::*;
use super::{Command, PrintJob, Printer};
pub fn printers() -> Vec<Printer> {
craft_print::printers().into_iter().map(|p| Printer { name: p.name, default: p.default }).collect()
}
+3 -1
View File
@@ -1,4 +1,6 @@
use super::*;
use super::window;
#[cfg(target_os = "linux")]
use super::xwayland;
/// The window, Dock, taskbar and app-switcher icon (`assets/app-icon/`, see its README). macOS gets
/// the version with Apple's transparent margin; elsewhere the full-bleed tile. The app ID matches
+1
View File
@@ -6,3 +6,4 @@ allow-panic-in-tests = true
disallowed-types = [
{ path = "egui::ComboBox", reason = "use craft_ui::ComboBox: egui's combo box menu can't grow back while open (egui#5225)" },
]
cognitive-complexity-threshold = 30
+1 -1
View File
@@ -1,5 +1,5 @@
use super::read::MAX_TREE_DEPTH;
use super::*;
use super::{Affine, Kind, Node, ObjId, Point, Rect, Stream, Tag};
/// The bounds of what visible `nodes` draw (control points of outlines, corners of images, frames
/// and tables), in document pixels.
+4 -1
View File
@@ -1,5 +1,8 @@
use super::bounds::{describe, is_container, mask_extent, nodes_bounds, pages, rect, tree_bounds, union};
use super::*;
use super::{
Affine, Archive, BTreeMap, Blend, Document, Effect, Error, HashSet, Image, Kind, Limits, Node, ObjId, Paint, Path, Point, Rect, Spread, Stream,
Tag, paint, stream,
};
/// Largest embedded colour profile kept (real CMYK profiles are a few megabytes).
const MAX_PROFILE_BYTES: usize = 32 << 20;
+1 -1
View File
@@ -1,6 +1,6 @@
use super::profile::{PressureProfile, WidthProfile};
use super::stroke_style::{ArrowAlign, Arrowhead, Dash, LineCap, LineJoin, StrokeAlign};
use super::*;
use super::{BlendMode, Deserialize, Paint, Serialize};
/// A live effect in an appearance stack. Parameters are interpreted by `vectorcraft-effects`.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Deserialize, Serialize};
/// Variable-width profile: (position 0..1 along the path, left width factor, right width factor),
/// in order along the path. Two points at the same position make a discontinuous point: the
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum LineCap {
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Arc, Deserialize, Node, NodeId, NodeKind, Rect, Serialize, SymbolBoxes, skip};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Artboard {
+4 -1
View File
@@ -3,7 +3,10 @@ use super::graphic_style::default_graphic_styles;
use super::guides::{GridPrefs, RulerMode};
use super::limits::{DocError, FORMAT_VERSION, MAX_COORD, MAX_NEXT_ID};
use super::units::{ColorMode, Unit};
use super::*;
use super::{
Arc, BTreeMap, ColorProfiles, DocMetadata, DocSetup, Document, LAYER_COLORS, LayerColor, Node, NodeId, NodeKind, PatternDef, Places, Point,
RasterEffectsSettings, Rect, Vec2,
};
impl Document {
/// Is everything but the layers the same in `other`, as the next version of the document after
+4 -1
View File
@@ -1,7 +1,10 @@
use super::graphic_style::GraphicStyle;
use super::limits::MAX_NEXT_ID;
use super::records::{ImageBlob, SavedSelection, SavedView};
use super::*;
use super::{
Arc, Document, ImageObject, Node, NodeKind, SYMBOL_EXPORT_TYPE, SYMBOL_NINE_SLICE, SYMBOL_REGISTRATION, SymbolExportType, SymbolNineSlice,
SymbolRegistration,
};
impl Document {
/// The pixels image `im` shows: its own ([`ImageObject::pixels`]) or this document's.
+1 -1
View File
@@ -1,6 +1,6 @@
use super::document_ext::free_id;
use super::guides::Guide;
use super::*;
use super::{Affine, Arc, Document, Node, NodeId, NodeKind, PatternDef, Point, Rect, Selection, SymbolBoxes, Vec2, artboard_holds};
impl Document {
/// Union of the geometric bounds of `ids` (their visual bounds when `visual`), with symbol
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Appearance, Deserialize, Knockout, Node, Rect, Serialize, skip};
/// A named graphic style (Graphic Styles panel): an appearance plus the object's transparency.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Deserialize, NodeId, Serialize, Vec2};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Guide {
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::NodeId;
/// Coordinates beyond this many points exceed the supported document range.
pub const MAX_COORD: f64 = 4.0e6;
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Deserialize, PathData, Serialize};
// =====================================================================================
// Data
+1 -1
View File
@@ -1,6 +1,6 @@
use super::expand::{Hooks, Outliner, Warper, map_node};
use super::mesh::{GradientMesh, MeshPoint, grid_len, grid_points, nodes_bounds, surface_of};
use super::*;
use super::{Affine, Arc, Color, Deserialize, Node, NodeId, NodeKind, PathData, Point, Rect, Serialize, Vec2, yes};
/// How an envelope distorts its content.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+4 -1
View File
@@ -1,7 +1,10 @@
use super::envelope::{EnvelopeOptions, expand_envelope};
use super::mesh::GradientMesh;
use super::warp::map_nonlinear;
use super::*;
use super::{
Affine, Appearance, AppearanceItem, Arc, FillLayer, ImageObject, Node, NodeId, NodeKind, Paint, PathData, Point, Rect, SubPath, Vec2,
blend_expand,
};
/// Optional hook that converts nodes the pure evaluation can't map (text) to outlines.
pub type Outliner<'a> = Option<&'a dyn Fn(&Node) -> Option<Node>>;
+4 -1
View File
@@ -1,6 +1,9 @@
use super::envelope::{EnvelopeKind, H_DOWN, H_LEFT, H_RIGHT, H_UP, envelope_grid, mesh_at};
use super::warp::{WarpStyle, flatten_subpath, resample, warp_point};
use super::*;
use super::{
Affine, Anchor, Arc, Color, CubicBez, Deserialize, Node, ParamCurve, PathData, Point, Rect, Serialize, Shape, SubPath, Vec2, fifty, lerp_color,
one,
};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MeshPoint {
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Anchor, BezPath, CubicBez, PI, ParamCurve, PathData, Point, SubPath};
// =====================================================================================
// Warp maps (shared with vectorcraft-effects' Warp effects)
+1 -1
View File
@@ -1,6 +1,6 @@
use super::kind::NodeKind;
use super::live_shape::LiveShape;
use super::*;
use super::{Deserialize, Node, Serialize};
/// Window → Attributes: an object's settings that don't change how it prints.
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
+1 -1
View File
@@ -1,5 +1,5 @@
use super::kind::NodeKind;
use super::*;
use super::{BezPath, FillRule, Node, Outliner, Rect, shapes};
/// Unites filled regions (each under its own fill rule) into one path filled non-zero. Booleans
/// live above this crate (`vectorcraft-pathops`), so callers supply it.
+1 -1
View File
@@ -2,7 +2,7 @@ use super::attributes::ObjectAttributes;
use super::kind::NodeKind;
use super::live_shape::LiveShape;
use super::opacity::OpacityMask;
use super::*;
use super::{Node, PathData};
/// The parts of a [`Node`] that few objects have: kept out of line, so that the nodes without
/// them stay small ([`Node::extras`]).
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Deserialize, Serialize};
/// Stable per-document object id. Never reused.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
+1 -1
View File
@@ -1,6 +1,6 @@
use super::ids::LayerColor;
use super::live_shape::LiveShape;
use super::*;
use super::{Affine, Arc, BlendSpec, Deserialize, EnvelopeKind, FillRule, GradientMesh, Node, PathData, RepeatSpec, Serialize, TextObject, yes};
/// Embedded or linked raster image.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Affine, CornerKind, Deserialize, PathData, Point, Rect, Serialize, cut_corners, shapes};
/// Live shape parameters (Illustrator's "Live Shapes"). The node's path is regenerated from these
/// while the shape stays live; editing anchors directly converts it to a plain path.
+1 -1
View File
@@ -1,5 +1,5 @@
use super::kind::NodeKind;
use super::*;
use super::{Arc, Color, Deserialize, Node, Paint, Serialize, yes};
/// Opacity mask: the luminance of the mask art sets the object's opacity (white = opaque).
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+1 -1
View File
@@ -3,7 +3,7 @@ use super::ids::{LayerColor, NodeId};
use super::kind::NodeKind;
use super::live_shape::{Scaling, moves_only};
use super::opacity::Knockout;
use super::*;
use super::{Affine, Appearance, Arc, BlendMode, EnvelopeKind, FillRule, Node, PathData, Point, Rect, RepeatSpec, SYMBOL_SQUARE, Shape, SymbolBoxes};
impl Node {
pub fn new(id: NodeId, kind: NodeKind) -> Self {
+1 -1
View File
@@ -3,7 +3,7 @@ use super::extras::{Extras, NO_EXTRAS, NodeExtras};
use super::ids::NodeId;
use super::kind::NodeKind;
use super::opacity::{Knockout, OpacityMask};
use super::*;
use super::{Appearance, BlendMode, Deserialize, Node, PathData, Serialize, one, yes};
/// How a [`Node`] is written: the flat field list of the file format, in its order, borrowed from
/// the node and its extras (nothing is copied). The node reads back through `NodeWire`.
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Arc, Deserialize, Node, NodeId, Point, Serialize};
/// New art on a locked artboard ([`Document::new_art`]). Setting it changes the document in
/// place, so a selection change that sets it doesn't replace the document.
+1 -1
View File
@@ -1,5 +1,5 @@
use super::para_style::FirstBaseline;
use super::*;
use super::{Deserialize, PathData, Serialize};
/// Area Type Options "Align" (vertical): where the lines of each row/column sit in it.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
+1 -1
View File
@@ -1,5 +1,5 @@
use super::para_style::ParaDirection;
use super::*;
use super::{Appearance, AppearanceItem, Color, Dash, Deserialize, FillLayer, LineCap, LineJoin, Paint, Serialize, StrokeAlign, StrokeLayer};
/// Character attributes (the Character panel).
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+1 -1
View File
@@ -3,7 +3,7 @@ use super::char_style::CharStyle;
use super::para_style::ParaStyle;
use super::runs::{RubySpan, TextKind, TextRun, move_anchors, reverse_from_end};
use super::tabs_lists::ParaList;
use super::*;
use super::{Affine, PathData, Point, Rect, TextObject, TextStyleScale, Vec2};
impl TextObject {
/// None for collapsed or unrepresentable transforms, whose type dimensions cannot
+1 -1
View File
@@ -1,6 +1,6 @@
use super::char_style::CharStyle;
use super::tabs_lists::{ParaList, TabStop};
use super::*;
use super::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum Justify {
+1 -1
View File
@@ -1,5 +1,5 @@
use super::char_style::CharStyle;
use super::*;
use super::{Affine, Deserialize, PathData, Point, Rect, Serialize, Vec2, yes};
/// A named character or paragraph style: the attributes it sets (a subset of [`CharStyle`] or
/// [`ParaStyle`] fields, by their serialized names). Text using it records the name.
+1 -1
View File
@@ -1,5 +1,5 @@
use super::char_style::{CharPosition, CharStyle};
use super::*;
use super::{Deserialize, Serialize};
/// Tab stop alignment (Tabs panel).
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Deserialize, Serialize};
/// Measurement units (display only; the model is always in points).
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{CROP_MARKS, EXTRUDE, INFLATE, PATHFINDER_EFFECTS, RASTERIZE, REVOLVE, ROTATE, TRAP, Value, json, trap};
/// Catalogue entry for one effect.
#[derive(Clone, Debug, PartialEq)]
+1 -1
View File
@@ -4,7 +4,7 @@ use serde_json::Value;
use vectorcraft_geom::kurbo::ParamCurveArclen;
use vectorcraft_geom::{Affine, Anchor, ParamCurve, PathData, Point, Rect, SubPath, Vec2};
use crate::util::*;
use crate::util::{catmull_rom, fit_piece, flag, map_nonlinear_within, mean_size, noise, normal_at, num, pieces, seed, seg_cubic, smooth_points};
fn diag(b: Rect) -> f64 {
b.width().hypot(b.height()).max(1e-6)
+5 -2
View File
@@ -1,5 +1,8 @@
use super::params::{is_geometry, merged_params};
use super::*;
use super::{
Appearance, AppearanceItem, BezPath, Effect, FillRule, Node, NodeKind, PathData, Rect, StrokeLayer, Value, WarpStyle, distort, stroke, stylize,
util, warp,
};
/// Context the geometry effects may need from the object's appearance.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
@@ -167,7 +170,7 @@ fn builds_within(id: &str) -> bool {
/// [`apply_one`] whose result may pass `max` when the effect doesn't build within it.
fn apply_unchecked(id: &str, p: &Value, path: &PathData, b: Rect, ctx: &GeomContext, max: usize) -> PathData {
use util::*;
use util::{join, num};
if let Some(plugin) = vectorcraft_plugins::effect::plugin_id(id) {
let paints = vectorcraft_plugins::effect::Paints { appearance: ctx.appearance, item: ctx.item };
return vectorcraft_plugins::effect::apply(plugin, p, path, b, ctx.rule, paints).unwrap_or_else(|| path.clone());
+1 -1
View File
@@ -1,5 +1,5 @@
use super::catalog::{catalog_index, plugin_info};
use super::*;
use super::{CROP_MARKS, Effect, Map, Node, PIXEL_EFFECTS, RASTERIZE, Value, is_adjustment, is_pathfinder, is_three_d, json, util};
/// Dialog defaults for `id` (`None` for unknown effects).
pub fn default_params(id: &str) -> Option<Value> {
+8
View File
@@ -1755,6 +1755,10 @@ fn brush_strokes_params_take_defaults_and_stay_in_range() {
fx("brushStrokes.sumiE", json!({"strokeWidth": 2, "strokePressure": 16, "contrast": -1})),
PixelFx::SumiE { width: 3.0, pressure: 15.0, contrast: 0.0 }
);
brush_strokes_outsets();
}
fn brush_strokes_outsets() {
// Outsets: the strokes and the shifts reach out of the object as far as into it; Accented
// Edges and Sumi-e keep the object's shape.
let b = Rect::new(0.0, 0.0, 100.0, 50.0);
@@ -2561,6 +2565,10 @@ fn artistic_params_take_defaults_and_stay_in_range() {
fx("artistic.plasticWrap", json!({"highlightStrength": -1, "detail": 99, "smoothness": 0})),
PixelFx::PlasticWrap { highlight: 0.0, detail: 15.0, smoothness: 1.0 }
);
artistic_outsets_and_docs();
}
fn artistic_outsets_and_docs() {
for id in [
"artistic.coloredPencil",
"artistic.cutout",
+1 -1
View File
@@ -9,7 +9,7 @@ use vectorcraft_geom::{Point, Rect};
use crate::merged_params;
use crate::pixel::{self, PixelFx, PixelSpace};
use crate::util::*;
use crate::util::{flag, num, text};
/// Effect › Rasterize's id.
pub const RASTERIZE: &str = "rasterize";
+1 -1
View File
@@ -5,7 +5,7 @@ use vectorcraft_geom::kurbo;
use vectorcraft_geom::shapes::{self, KAPPA};
use vectorcraft_geom::{Affine, Anchor, PathData, Point, Rect, SubPath, corners};
use crate::util::*;
use crate::util::{catmull_rom, flag, noise, num, seed};
/// Convert to Shape (rectangle / rounded rectangle / ellipse) around the bounds.
pub fn convert_to_shape(id: &str, b: Rect, p: &Value) -> PathData {
+1 -1
View File
@@ -6,7 +6,7 @@
use serde_json::Value;
use vectorcraft_geom::{PathData, Point, Rect};
use crate::util::*;
use crate::util::{map_nonlinear_within, num, text};
pub use vectorcraft_doc::live::{WarpStyle, warp_point};
+4 -1
View File
@@ -28,8 +28,11 @@ use vectorcraft_geom::Rect;
use super::edit::{outermost, selected_roots};
use super::opacitymask::percent;
use super::paint::paint_from;
use super::*;
use super::{AppearanceAttrs, EyedropperAttrs, FillAttrs, StrokeAttrs};
use super::{
CommandSpec, NodeId, Result, Session, bad, check_numbers, check_switches, cmd, has_doc, has_selection, id_param, ids_param, index, leaf_targets,
ok, paint_targets, str_param, switch_or, targets,
};
use crate::EngineError;
pub fn specs() -> Vec<CommandSpec> {
+1 -1
View File
@@ -8,7 +8,7 @@ use vectorcraft_doc::{ExportAsset, NodeId};
use super::edit::{roots_of, selected_roots};
use super::fileio::{SCREEN_SETTINGS_KEYS, check_screen_settings, export_crop, export_screens, store_screen_settings};
use super::*;
use super::{CommandSpec, EngineError, Result, Session, bad, bool_or, checked_ids_param, cmd, has_doc, ok, str_param};
/// The longest asset name kept (characters).
const MAX_NAME: usize = 255;
+1 -1
View File
@@ -10,7 +10,7 @@ use vectorcraft_geom::FillRule;
use super::overprint::{AttributesInfo, apply_overprint, edit_counted, overprint_jobs};
use super::path::paths_in;
use super::*;
use super::{CommandSpec, NodeId, Result, Session, bad, cmd, has_doc, str_param, targets};
/// How many URLs the Attributes panel's recent list keeps.
const RECENT_URLS: usize = 10;
+1 -1
View File
@@ -22,7 +22,7 @@ use super::clipboard::resources::SwatchChoices;
use super::patterncmds::add_pattern;
use super::swatch::str_list;
use super::swatchlib::{FileData, LibraryFile, LibraryInfo, stem};
use super::*;
use super::{CommandSpec, Result, Session, always, bad, bool_or, cmd, has_doc, str_param};
/// The extension of saved brush libraries: native documents.
pub const BRUSHES_EXT: &str = vectorcraft_format::EXTENSION;
+4 -1
View File
@@ -18,7 +18,10 @@ use vectorcraft_geom::{Affine, Point, Vec2};
use super::edit::{outermost, selected_roots};
use super::pathops::{remove_each, separate};
use super::swatch::{anchor, names_param};
use super::*;
use super::{
CommandSpec, Result, Session, bad, check_numbers, cmd, color_value, find_command, has_doc, has_selection, id_param, ids_param, number_or, ok,
str_param, switch_or,
};
use crate::EngineError;
mod brushes;
+5 -1
View File
@@ -1,4 +1,8 @@
use super::*;
use super::{
AppearanceItem, Arc, BTreeSet, Bristle, Brush, BrushKind, Color, Document, EngineError, Node, NodeId, NodeKind, Paint, Result, Session, Value,
anchor, bad, brush, check_numbers, find_command, id_param, ids_json, json, name_param, names_param, ok, roots, selection_art, set_unknown,
str_param,
};
// ---------- brushes ----------
+5 -1
View File
@@ -1,4 +1,8 @@
use super::*;
use super::{
Affine, AppearanceItem, Arc, CURRENT_SYMBOL, Color, Document, EngineError, FillLayer, HALF, Node, NodeId, NodeKind, Paint, Point, Result,
SIZES_KEY, Session, Symbol, SymbolExportType, SymbolNineSlice, SymbolRegistration, Value, Vec2, bad, brush, color_value, ids_json, json,
name_param, number_or, ok, remove_each, roots, selection_art, separate, set_unknown, str_param, switch_or,
};
// ---------- symbols ----------
+4 -1
View File
@@ -27,7 +27,10 @@ use super::draw2::replace_with_pieces;
use super::edit::{outermost, selected_roots};
use super::paint::paint_from;
use super::pathops::{Beside, shape_node};
use super::*;
use super::{
CommandSpec, EngineError, Result, Session, always, bad, check_numbers, cmd, color_value, f64_or, finite_numbers, has_doc, id_param, ids_param,
number_or, point_param, str_param, switch_or,
};
mod live_paint;
mod shape_builder;
@@ -1,4 +1,8 @@
use super::*;
use super::{
Appearance, Arc, Beside, Document, EDGE_NAME, EngineError, FACE_NAME, GapOptions, GapSize, LIVE_PAINT_NAME, Node, NodeId, Paint, PathData, Point,
Rect, Result, SOURCES_NAME, Selection, Session, SubPath, Value, b, bad, build_children, edge_near, face_at, finite, id_param, ids_json,
is_live_paint, json, number_or, paint_from, po, point_param, repaint_from, replace_roots, root_ids, source_nodes, source_shapes, switch_or,
};
/// `options` as stored on a Live Paint group: none for the defaults.
fn stored_gap_options(options: GapOptions) -> Option<Box<GapOptions>> {
@@ -1,4 +1,8 @@
use super::*;
use super::{
Beside, BuilderMap, EngineError, FillRule, Node, NodeId, PathData, Point, Rect, Result, Session, SubPath, Touched, Value, b, bad, check_numbers,
f64_or, fill_param, finite, gap_param, ids_json, json, marquee_param, po, points_param, remnant, replace_roots, replace_with_pieces, root_ids,
shape_node, switch_or,
};
pub(super) fn sb_merge(s: &mut Session, p: &Value) -> Result<Value> {
const C: &str = "shapeBuilder.merge";
+4 -1
View File
@@ -1,4 +1,7 @@
use super::*;
use super::{
Appearance, AppearanceItem, Arc, Beside, Color, Document, EngineError, LineCap, LineJoin, LiveShape, Node, NodeId, NodeKind, Paint, Point,
Result, Session, StrokeLayer, TRACE_NAME, TraceView, Value, bad, fill_only, ids_json, json, root_ids, shape_node, str_param, switch_or, tr,
};
// ---------- Image Trace ----------
+1 -1
View File
@@ -25,7 +25,7 @@ pub use flavours::{AI, BITMAP, EMF, EPS, FILE_HEAD, Flavour, PASTE_ORDER, PDF, P
pub(crate) use resources::SwatchChoices;
pub use resources::{ArtResources, art_resources};
use super::*;
use super::{CommandSpec, Result, Session, always, bad, cmd, f64_or, has_doc, point_param, str_param};
use crate::DocState;
pub fn specs() -> Vec<CommandSpec> {
+1 -1
View File
@@ -12,7 +12,7 @@ use vectorcraft_geom::{Affine, Point};
use super::super::fileio;
use super::super::place::pt_per_px;
use super::*;
use super::{Clipboard, CommandSpec, Result, Session, always, bad, cmd, f64_or, has_doc, looks_like_svg, point_param, str_param};
/// Plain text: a type-only copy's text, else the SVG markup (Include SVG Code).
pub const TEXT: &str = "text/plain";
+3 -1
View File
@@ -18,7 +18,9 @@ use vectorcraft_doc::recolor::{ColorVisitor, Reach, map_links, recolor_node};
use vectorcraft_doc::{Document, ImageBlob, Node, NodeId, NodeKind};
use super::edit::selected_roots;
use super::*;
use super::{
CommandSpec, EngineError, Result, Session, always, bad, bool_or, cmd, color_value, f64_or, has_selection, ids_param, str_param, unique_name,
};
pub fn specs() -> Vec<CommandSpec> {
vec![
+3 -1
View File
@@ -17,7 +17,9 @@ use vectorcraft_color::{Color, Paint};
use vectorcraft_doc::ColorMode;
use vectorcraft_render::proof;
use super::*;
#[cfg(not(target_arch = "wasm32"))]
use super::EngineError;
use super::{CommandSpec, Result, Session, always, bad, bool_or, cmd, color_value, has_doc, str_param, targets};
/// `Document.unknown` key under which files before format v3 kept the assigned profiles (migrated
/// to `Document::color_profiles` on load).
+1 -1
View File
@@ -9,7 +9,7 @@ use vectorcraft_color::harmony::{Harmony, THEME_SIZE};
use vectorcraft_color::recolor::ColorKey;
use vectorcraft_tools::params::color_json;
use super::*;
use super::{CommandSpec, Result, Session, always, bad, cmd, color_value, has_doc, str_param};
/// A saved colour theme ([`crate::Prefs::color_themes`]).
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+4 -1
View File
@@ -5,7 +5,10 @@ use vectorcraft_doc::{Appearance, CharStyle, LiveShape, Node, NodeKind, TextObje
use vectorcraft_geom::{Affine, Anchor, AnchorKind, FillRule, PathData, Point, Rect, SubPath, shapes};
use super::newart::NewArt;
use super::*;
use super::{
CommandSpec, NodeId, Result, Session, bad, bool_or, check_numbers, check_switches, cmd, color_value, f64_or, f64_req, finite_numbers, has_doc,
number_or, str_param, switch_or,
};
pub fn specs() -> Vec<CommandSpec> {
vec![
+1 -1
View File
@@ -8,7 +8,7 @@ use vectorcraft_doc::NodeId;
use vectorcraft_svg::{CssOptions, CssSheet, css_rules};
use super::fileio::{default_name, encode_with_warnings, isolated, write_file};
use super::*;
use super::{CommandSpec, Result, Session, bad, cmd, has_doc, str_param, targets};
pub fn specs() -> Vec<CommandSpec> {
vec![
+1 -1
View File
@@ -15,7 +15,7 @@ use vectorcraft_pathops::BoolOp;
use super::draw2::{components, erase_outline, ids_json, replace_with_pieces};
use super::edit::selected_roots;
use super::pathops::{Beside, node_path};
use super::*;
use super::{CommandSpec, Result, Session, bad, cmd, finite_numbers, has_selection, point_param, str_param};
pub fn specs() -> Vec<CommandSpec> {
vec![
+1 -1
View File
@@ -1,5 +1,5 @@
//! Linked dimensions: generated groups share the existing text/path render and export pipeline.
use super::*;
use super::{CommandSpec, EngineError, Result, Session, cmd, has_doc};
use serde_json::{Value, json};
use std::sync::Arc;
use vectorcraft_color::{Color, Paint};
+4 -1
View File
@@ -14,7 +14,10 @@ use vectorcraft_tools::distort::perspective::{self as persp, PerspectiveGrid, Pl
use vectorcraft_tools::distort::{PinSet, arap, collect_points, mesh_for, project_node, stroke_owner, warp_from_rest, warp_node_with};
use super::edit::{duplicate_in, outermost, selected_roots};
use super::*;
use super::{
CommandSpec, Result, Session, bad, check_numbers, check_switches, cmd, f64_or, f64_req, finite_numbers, has_doc, has_selection, id_param,
ids_param, matrix_param, number_or, ok, point_indices, point_param, str_param, switch_or, targets,
};
use crate::EngineError;
mod perspective;
@@ -1,4 +1,8 @@
use super::*;
use super::{
Affine, Arc, Document, EngineError, Homography, NodeId, NodeKind, PerspectiveGrid, Plane, Point, Result, Session, Value, bad, check_numbers,
check_switches, collect_points, duplicate_in, finite_numbers, id_param, ids_param, json, matrix_param, number_or, ok, outermost, persp,
point_param, project_node, selected_roots, str_param, switch_or,
};
// ---------- perspective grid ----------
+1 -1
View File
@@ -9,7 +9,7 @@ use vectorcraft_doc::{ColorMode, Document, Node, NodeId, NodeKind, RepeatKind, R
use vectorcraft_geom::{Affine, Rect};
use vectorcraft_text::embed::Embedding;
use super::*;
use super::{CommandSpec, Result, Session, bad, bool_or, cmd, has_doc, has_selection, str_param, targets};
pub fn specs() -> Vec<CommandSpec> {
vec![
+4 -1
View File
@@ -12,7 +12,10 @@ use vectorcraft_geom::Vec2;
use super::edit::selected_roots;
use super::pathops::Beside;
use super::select::{selectable_ids, walk_selectable};
use super::*;
use super::{
CommandSpec, EngineError, Point, Result, Session, bad, bool_or, checked_ids_param, cmd, f64_or, f64_req, has_doc, has_selection, id_param, ok,
str_param,
};
pub fn specs() -> Vec<CommandSpec> {
vec![
+1 -1
View File
@@ -7,7 +7,7 @@ use vectorcraft_doc::{Background, CharPosition, CharStyle, DocSetup, Document, E
use super::flatten::{FlattenOptions, FlattenerPreset};
use super::*;
use super::{CommandSpec, Result, Session, bad, cmd, color_value, has_doc};
const C: &str = "document.setup";
+4 -1
View File
@@ -17,7 +17,10 @@ use vectorcraft_tools::draw2::{eraser_area, eraser_tip};
use super::create::{add_look, insert_look};
use super::newart::NewArt;
use super::pathops::{Beside, remove_each};
use super::*;
use super::{
CommandSpec, Result, Session, bad, check_numbers, cmd, f64_or, has_anchors, has_doc, has_selection, id_param, index, number_or, ok, str_param,
switch_or,
};
use crate::EngineError;
mod anchors;
+4 -1
View File
@@ -1,5 +1,8 @@
use super::common::{MAX_INPUT, path_kind, path_mut, point_entry, usize_opt, usize_req, within_input_bound};
use super::*;
use super::{
Anchor, BTreeSet, Beside, Color, Document, EngineError, Node, NodeId, NodeKind, Paint, PathData, Point, Result, Selection, Session, Value, Vec2,
add_look, bad, check_numbers, f64_or, id_param, json, number_or, ok, po, str_param, switch_or,
};
// ---------- Curvature ----------
+1 -1
View File
@@ -2,7 +2,7 @@ use super::anchors::{convert_anchor, curvature, curvature_edit, remove_anchor, r
use super::cut::{erase_region, erase_segments, knife, split};
use super::freehand::{blob, freehand};
use super::smooth::{join_scrub, smooth_region};
use super::*;
use super::{CommandSpec, cmd, has_anchors, has_doc, has_selection};
pub fn specs() -> Vec<CommandSpec> {
vec![
+4 -1
View File
@@ -1,4 +1,7 @@
use super::*;
use super::{
Appearance, Arc, Beside, Cap, Color, Document, EngineError, FillRule, Join, Node, NodeId, NodeKind, Paint, PathData, Point, PressureProfile,
Rect, Result, Selection, Session, SubPath, Value, Vec2, bad, index, json, po,
};
// ---------- helpers ----------
+4 -1
View File
@@ -1,7 +1,10 @@
use super::common::{
dist_to_polyline, ids_json, path_mut, path_of, points_param, poly_bounds, replace_with_pieces, sibling_with, target_paths, usize_opt,
};
use super::*;
use super::{
Anchor, AnchorKind, Beside, BezPath, BoolOp, Cap, EngineError, FillRule, Join, NodeId, ParamCurve, PathData, Point, Result, Session, SubPath,
Value, bad, eraser_area, eraser_tip, f64_or, fill_contains, id_param, json, number_or, po,
};
/// Split a filled path into connected pieces (each outer contour with its holes).
pub(in super::super) fn components(pd: &PathData) -> Vec<PathData> {
+4 -1
View File
@@ -2,7 +2,10 @@ use super::common::{
brush_region, path_kind, path_mut, path_of, points_param, poly_bounds, samples_param, stroke_paint, stroke_pressure, subpath_len, target_paths,
within_input_bound,
};
use super::*;
use super::{
Anchor, Appearance, BTreeSet, BoolOp, Color, EngineError, FillRule, NewArt, NodeId, NodeKind, Paint, PathData, Point, PressureProfile, Result,
Selection, Session, SimplifyOptions, SubPath, Value, bad, id_param, insert_look, json, number_or, po, remove_each, str_param, switch_or,
};
// ---------- Pencil / Paintbrush ----------
+4 -1
View File
@@ -1,5 +1,8 @@
use super::common::{dist_to_polyline, ids_json, path_mut, path_of, points_param, poly_bounds, target_paths};
use super::*;
use super::{
Anchor, BezPath, CubicBez, NodeId, ParamCurve, ParamCurveNearest, PathData, Point, Result, Session, SimplifyOptions, SubPath, Value, json,
number_or, po,
};
// ---------- Smooth ----------
+5 -1
View File
@@ -8,7 +8,11 @@ use vectorcraft_doc::{Document, Node, NodeId, RulerMode, Unit};
use vectorcraft_geom::{Affine, Point, Vec2};
use super::clipboard::SwatchChoices;
use super::*;
use super::{
CommandSpec, Result, Session, always, bad, bool_or, can_clear_history, can_redo, can_undo, checked_ids_param, cmd, f64_or, f64_req,
finite_numbers, has_clipboard, has_doc, has_selection, has_selection_or_artboard_tool, has_selection_or_guides, id_param, index, ok, point_param,
str_param,
};
use crate::{Clipboard, EngineError, inspect};
/// Register editing commands, including document-to-document artwork drops and their parameter contracts.
+1 -1
View File
@@ -15,7 +15,7 @@ use vectorcraft_render::effects;
use super::appearance::{ItemTarget, aligned, appearance_targets, index_param, item_target, item_target_at, shared_stack};
use super::expand::has_seeded;
use super::rasterfx;
use super::*;
use super::{CommandSpec, EngineError, NodeId, Result, Session, always, bad, cmd, fileio, has_doc, str_param};
pub fn specs() -> Vec<CommandSpec> {
vec![
+1 -1
View File
@@ -22,7 +22,7 @@ use super::edit::selected_roots;
use super::menucmds::{run_raw, squash};
use super::object::as_clipping_path;
use super::pathops::{assemble, node_path, outline_strokes, replace_leaves, styled_copy, with_transparency};
use super::*;
use super::{CommandSpec, EngineError, Result, Session, bad, bool_or, cmd, f64_or, has_selection, str_param};
const C: &str = "object.expand";
/// Expand Gradient To → Specify … Objects: the default and the most.
+1 -1
View File
@@ -7,7 +7,7 @@ use serde_json::{Map, Value, json};
use vectorcraft_doc::metadata::{MAX_RATING, iso8601};
use vectorcraft_doc::{ColorMode, CopyrightStatus, DocMetadata, Document};
use super::*;
use super::{Result, Session, bad};
const C: &str = "file.info";
/// Longest text field, in characters.
+1 -1
View File
@@ -92,7 +92,7 @@ pub use svg::options_map as svg_options;
#[cfg(not(target_arch = "wasm32"))]
pub use vectorcraft_format::write_atomic;
use super::*;
use super::{CommandSpec, Result, Session, always, bad, cmd, has_doc, has_selection};
use crate::EngineError;
mod access;
+3 -1
View File
@@ -1,4 +1,6 @@
use super::*;
#[cfg(not(target_arch = "wasm32"))]
use super::write_atomic;
use super::{EngineError, Result, Session, Value, bad, json};
// ---------- the file system (none on the web, where commands take and return bytes) ----------
//
+3 -1
View File
@@ -9,7 +9,9 @@ use serde_json::Value;
#[cfg(not(target_arch = "wasm32"))]
use serde_json::json;
use super::super::*;
use super::super::{Result, Session, bad};
#[cfg(not(target_arch = "wasm32"))]
use super::super::{bool_or, find_command, str_param};
#[cfg(target_arch = "wasm32")]
pub(super) fn action_batch(_: &mut Session, _: &Value) -> Result<Value> {
@@ -1,7 +1,7 @@
use super::layout::{master_instances, master_prefix, page_masters};
use super::master_copies::{CopyCost, MAX_MASTER_COPIES, MasterPart, copy_cost};
use super::nodes::rect_of;
use super::*;
use super::{Affine, Arc, Artboard, Blend, Builder, Document, Kind, Node, NodeKind, Result, af, err};
impl Builder {
pub(super) fn new(k: f64) -> Self {
@@ -1,4 +1,4 @@
use super::*;
use super::{Builder, Cursor, Document, Result, err, raster_doc};
/// What an Affinity file opened as.
pub(in super::super) struct Imported {
@@ -1,5 +1,5 @@
use super::nodes::MAX_DEPTH;
use super::*;
use super::{AfAlign, CharStyle, Cursor, Justify, Kind, Rect, TextKind, TextObject, VerticalAlign, af, shapes};
/// The master page instances among `nodes` (also inside layers and groups): (master, first page,
/// first master page, page count).
@@ -1,5 +1,5 @@
use super::nodes::MAX_DEPTH;
use super::*;
use super::{Kind, af};
/// What copying a subtree takes: its nodes, characters and table cells (`work`), and its pixels.
#[derive(Clone, Copy, Debug, Default)]
@@ -1,4 +1,4 @@
use super::*;
use super::{Affine, Arc, Blend, BlendMode, Builder, FillRule, Kind, LayerColor, Node, NodeKind, OpacityMask, Rect, af};
/// Deepest node tree mapped (the reader bounds trees well below this).
pub(super) const MAX_DEPTH: usize = 160;
@@ -1,7 +1,11 @@
use super::layout::png;
use super::nodes::affine;
use super::text::Placement;
use super::*;
use super::{
AfKind, Affine, Appearance, AppearanceItem, Arc, BezPath, Builder, Cap, Color, Dash, Effect, FillLayer, Gradient, GradientGeom, GradientKind,
GradientPaint, GradientStop, ImageObject, Join, LineCap, LineJoin, NodeKind, Paint, PathData, Pixels, Point, StrokeAlign, StrokeLayer,
TextObject, Vec2, af, ap, json,
};
impl Builder {
/// Drop shadows, inner shadows and outer glows as VectorCraft's Stylize effects. Affinity's
@@ -1,6 +1,9 @@
use super::layout::{align_lines, fit_lines, font_style, justify, line_gap, place_cell};
use super::nodes::affine;
use super::*;
use super::{
Affine, Appearance, AppearanceItem, Arc, BezPath, Builder, CharStyle, FillRule, FirstBaseline, Node, NodeKind, Paint, PathData, Point, Rect,
TextKind, TextObject, TextRun, Vec2, VerticalAlign, af, ap, shapes,
};
/// Where text goes: a rigid placement (translation and rotation) and the scale that maps node
/// space into it (applied to the frame's geometry, not the type), plus the type's own scale.
+1 -1
View File
@@ -5,7 +5,7 @@ use std::sync::Arc;
use serde_json::{Value, json};
use vectorcraft_doc::Document;
use super::super::*;
use super::super::{Result, Session, bad, str_param};
use crate::{DocState, EngineError};
pub(super) fn batch(s: &mut Session, p: &Value) -> Result<Value> {
+1 -1
View File
@@ -1,6 +1,6 @@
use super::access::file_list;
use super::formats::formats;
use super::*;
use super::{CommandSpec, action_batch, always, batch, cmd, export, has_doc, has_selection, load, pdfimport, save, screens};
pub fn specs() -> Vec<CommandSpec> {
vec![

Some files were not shown because too many files have changed in this diff Show More