mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-27 19:27:21 -06:00
Brushes: New Brush and Bristle Brush Options take a bristle brush name of at most 31 characters
Squashed from #3312 onto main.
This commit is contained in:
@@ -10,7 +10,7 @@ use std::collections::BTreeSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_brush::{self as brush, Brush};
|
||||
use vectorcraft_brush::{self as brush, Bristle, Brush, BrushKind};
|
||||
use vectorcraft_color::{Color, Paint};
|
||||
use vectorcraft_doc::{AppearanceItem, Document, FillLayer, Node, NodeId, NodeKind, Symbol, SymbolExportType, SymbolNineSlice, SymbolRegistration};
|
||||
use vectorcraft_geom::{Affine, Point, Vec2};
|
||||
@@ -72,7 +72,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"New Brush…",
|
||||
[],
|
||||
None,
|
||||
"{type: calligraphic|scatter|art|pattern|bristle, name? (trimmed; blank: New <Type> Brush), params?: {…definition fields}, ids?|id?} new brush; art/scatter/pattern brushes take the selected art (pattern: side tile) unless params give it → {name}",
|
||||
"{type: calligraphic|scatter|art|pattern|bristle, name? (trimmed; blank: New <Type> Brush; a bristle brush name, with the number a taken name gets, has at most 31 characters), params?: {…definition fields}, ids?|id?} new brush; art/scatter/pattern brushes take the selected art (pattern: side tile) unless params give it → {name}",
|
||||
has_doc,
|
||||
brush_new
|
||||
),
|
||||
@@ -117,7 +117,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Brush Options…",
|
||||
[],
|
||||
None,
|
||||
"{name, params?: {…fields to change (brush.get shows them; values outside their ranges are clamped). calligraphic: angle −180..180°, roundness 0..100%, size 0..1296 pt, variation: [angle°, roundness%, size pt], modes: [fixed|random|pressure for angle, roundness, size] (pressure goes from value − variation at the lightest pen pressure to value + variation at the heaviest). scatter: size [a, b] 1..10000%, spacing [a, b] 1..10000%, scatter [a, b] −1000..1000%, rotation [a, b] −180..180°, modes: [fixed|random|pressure for size, spacing, scatter, rotation] (fixed: a; random: between a and b; pressure: a at the lightest pen pressure to b at the heaviest), rotation_relative_to_path. art: width 1..1000%, scale: {mode: proportional|stretch|betweenGuides, start, end: 0..1 of the art length}, direction: leftToRight|rightToLeft|topToBottom|bottomToTop, flip_along, flip_across, overlap. pattern: scale 1..10000%, spacing 0..10000%, fit: stretch|addSpace|approximate, flip_along, flip_across. scatter, art and pattern: colorization: {method: none|tints|tintsAndShades|hueShift, key: colour (hueShift: the art colour that becomes the stroke colour; without a key: the solid color the art's fills and strokes use most, black when they use none)}; colorization and the art brush's scale change only the fields given}, newName? (trimmed; blank: the name is kept)} edit a brush definition (strokes using it update) → {name}",
|
||||
"{name, params?: {…fields to change (brush.get shows them; values outside their ranges are clamped). calligraphic: angle −180..180°, roundness 0..100%, size 0..1296 pt, variation: [angle°, roundness%, size pt], modes: [fixed|random|pressure for angle, roundness, size] (pressure goes from value − variation at the lightest pen pressure to value + variation at the heaviest). scatter: size [a, b] 1..10000%, spacing [a, b] 1..10000%, scatter [a, b] −1000..1000%, rotation [a, b] −180..180°, modes: [fixed|random|pressure for size, spacing, scatter, rotation] (fixed: a; random: between a and b; pressure: a at the lightest pen pressure to b at the heaviest), rotation_relative_to_path. art: width 1..1000%, scale: {mode: proportional|stretch|betweenGuides, start, end: 0..1 of the art length}, direction: leftToRight|rightToLeft|topToBottom|bottomToTop, flip_along, flip_across, overlap. pattern: scale 1..10000%, spacing 0..10000%, fit: stretch|addSpace|approximate, flip_along, flip_across. scatter, art and pattern: colorization: {method: none|tints|tintsAndShades|hueShift, key: colour (hueShift: the art colour that becomes the stroke colour; without a key: the solid color the art's fills and strokes use most, black when they use none)}; colorization and the art brush's scale change only the fields given}, newName? (trimmed; blank: the name is kept; a new bristle brush name has at most 31 characters, and a longer name the brush has is kept)} edit a brush definition (strokes using it update) → {name}",
|
||||
has_doc,
|
||||
brush_options
|
||||
),
|
||||
@@ -501,6 +501,9 @@ fn brush_new(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let given = str_param(p, "name").map(str::trim).filter(|n| !n.is_empty());
|
||||
let base_name = given.map_or_else(|| format!("New {} Brush", ty[..1].to_uppercase() + &ty[1..]), str::to_string);
|
||||
let name = brush::unique_name(&lib, &base_name);
|
||||
if ty == "bristle" {
|
||||
bristle_name_fits(C, &name)?;
|
||||
}
|
||||
let mut v = json!({"name": name, "type": ty});
|
||||
if let Some(params) = p.get("params") {
|
||||
merge(&mut v, params);
|
||||
@@ -521,6 +524,15 @@ fn brush_new(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok(json!({ "name": name }))
|
||||
}
|
||||
|
||||
/// Refuses `name` for a bristle brush when it has more than [`Bristle::MAX_NAME_CHARS`] characters.
|
||||
fn bristle_name_fits(cmd: &str, name: &str) -> Result<()> {
|
||||
let (count, max) = (name.chars().count(), Bristle::MAX_NAME_CHARS);
|
||||
if count > max {
|
||||
return Err(bad(cmd, format!("a bristle brush name can have at most {max} characters: `{name}` has {count}")));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A brush definition from command params, its values kept within their ranges ([`Brush::from_json`]).
|
||||
fn parse_brush(cmd: &str, v: Value) -> Result<Brush> {
|
||||
Brush::from_json(v).map_err(|e| bad(cmd, format!("invalid brush: {e}")))
|
||||
@@ -659,6 +671,10 @@ fn brush_options(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if lib.iter().any(|b| &b.name == nn) {
|
||||
return Err(bad(C, format!("a brush named `{nn}` exists")));
|
||||
}
|
||||
// A `newName` that only trims the white space around the saved name is taken at any length.
|
||||
if matches!(lib[i].kind, BrushKind::Bristle(_)) && nn != name.trim() {
|
||||
bristle_name_fits(C, nn)?;
|
||||
}
|
||||
v["name"] = json!(nn);
|
||||
} else {
|
||||
v["name"] = json!(name);
|
||||
|
||||
Reference in New Issue
Block a user