mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Merge pull request #2128 from switch87/feat/2127-gradient-span
paint.setFill / setStroke: one gradient across the targets (span: "selection") (#2127)
This commit is contained in:
@@ -16,7 +16,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Fill",
|
||||
[],
|
||||
None,
|
||||
"{color?: \"#rrggbb\"|[r,g,b]|{c,m,y,k}|{gray}|{l,a,b} (CIE Lab), none?: true, swatch?: name (a global or spot colour stays linked, so swatch edits recolour it; a tint swatch links to its base at its tint; a gradient swatch is recorded as the gradient's swatch and fits each object, keeping its aspect; the built-in \"[Registration]\" prints on every plate), tint?: 0..100 (% of a global or spot `swatch`; default 100, or a tint swatch's own), gradient?: {kind?: linear|radial|freeform, stops?: [{offset 0..1, color, opacity? 0..1 (or 0..100), midpoint? 0.13..0.87, swatch?: global or spot colour (or tint) swatch the stop links to (its colour comes from the swatch), tint?: 0..100}] (at least 2; default white→black), angle?: deg, start?: [x,y], end?: [x,y] (the vector in document coordinates, both or neither; type objects keep it in text space), aspect?: % (radial; without start/end the gradient is placed on each object's bounds), focal?: [x,y] (radial, with start/end: the focal point, where the first stop sits), swatch?: linked gradient swatch name}, item?: appearance item index|null (omitted: the Appearance panel's active item if it is a fill, else the top fill), ids?|id?, focus?: true (false keeps the active proxy), keepModel?: false (in a CMYK document, RGB colours and gradient stops are stored as CMYK unless true; Gray stays Gray), target?: \"object\"|\"contents\"} sets the selection's fill and the default (new art fits a gradient to itself); a solid colour recolours only the gradient mesh point last clicked with the Mesh tool or Direct Selection; while the Type tool has characters selected (and no ids or item are given), the paint goes to those characters only",
|
||||
"{color?: \"#rrggbb\"|[r,g,b]|{c,m,y,k}|{gray}|{l,a,b} (CIE Lab), none?: true, swatch?: name (a global or spot colour stays linked, so swatch edits recolour it; a tint swatch links to its base at its tint; a gradient swatch is recorded as the gradient's swatch and fits each object, keeping its aspect; the built-in \"[Registration]\" prints on every plate), tint?: 0..100 (% of a global or spot `swatch`; default 100, or a tint swatch's own), gradient?: {kind?: linear|radial|freeform, stops?: [{offset 0..1, color, opacity? 0..1 (or 0..100), midpoint? 0.13..0.87, swatch?: global or spot colour (or tint) swatch the stop links to (its colour comes from the swatch), tint?: 0..100}] (at least 2; default white→black), angle?: deg, start?: [x,y], end?: [x,y] (the vector in document coordinates, both or neither; type objects keep it in text space), aspect?: % (radial; without start/end the gradient is placed on each object's bounds), focal?: [x,y] (radial, with start/end: the focal point, where the first stop sits), swatch?: linked gradient swatch name}, item?: appearance item index|null (omitted: the Appearance panel's active item if it is a fill, else the top fill), ids?|id?, focus?: true (false keeps the active proxy), keepModel?: false (in a CMYK document, RGB colours and gradient stops are stored as CMYK unless true; Gray stays Gray), target?: \"object\"|\"contents\", span?: \"object\" (default: each target fits a gradient to its own bounds)|\"selection\" (a linear or radial `gradient` without start/end is placed once on the union of the targets' bounds, so it runs across them)} sets the selection's fill and the default (new art fits a gradient to itself); a solid colour recolours only the gradient mesh point last clicked with the Mesh tool or Direct Selection; while the Type tool has characters selected (and no ids or item are given), the paint goes to those characters only",
|
||||
has_doc,
|
||||
|s, p| set_paint(s, p, true)
|
||||
),
|
||||
@@ -381,6 +381,7 @@ fn apply_to_proxy(s: &mut Session, p: &Value, paint: Paint) -> Result<Value> {
|
||||
/// remember the paint.
|
||||
fn apply_paint(s: &mut Session, p: &Value, paint: Paint, fill: bool) -> Result<Value> {
|
||||
let cmd = if fill { "paint.setFill" } else { "paint.setStroke" };
|
||||
let span = spans_targets(p, &paint).map_err(|e| bad(cmd, e))?;
|
||||
// Characters selected with the Type tool take the paint, not the whole type object (#1063).
|
||||
if p.get("ids").is_none()
|
||||
&& p.get("item").is_none()
|
||||
@@ -415,6 +416,7 @@ fn apply_paint(s: &mut Session, p: &Value, paint: Paint, fill: bool) -> Result<V
|
||||
let item = item_target(s, p, cmd)?.of_kind(s, fill);
|
||||
let selected = item.targets(s, p)?;
|
||||
let ids = super::graph::paint_targets(&s.doc()?.doc, &selected);
|
||||
let paint = if span { placed_across(&s.doc()?.doc, &ids, paint, p, fill) } else { paint };
|
||||
// The gradient mesh point last clicked with the Mesh tool or Direct Selection: a solid fill
|
||||
// recolours just that point of its mesh.
|
||||
let mesh_point = if fill && matches!(paint, Paint::Solid { .. }) { focused_mesh_point(s) } else { None };
|
||||
@@ -494,6 +496,52 @@ fn apply_paint(s: &mut Session, p: &Value, paint: Paint, fill: bool) -> Result<V
|
||||
ok()
|
||||
}
|
||||
|
||||
/// Does `p` ask for `span: "selection"` (#2127)? Only a linear or radial gradient the targets
|
||||
/// would each fit to their own bounds can span them: one with its own vector (`start`/`end`),
|
||||
/// a freeform one or a gradient swatch already has its placement.
|
||||
fn spans_targets(p: &Value, paint: &Paint) -> std::result::Result<bool, String> {
|
||||
match p.get("span") {
|
||||
None | Some(Value::Null) => return Ok(false),
|
||||
Some(Value::String(v)) if v == "object" => return Ok(false),
|
||||
Some(Value::String(v)) if v == "selection" => {}
|
||||
Some(v) => return Err(format!("span must be \"object\" or \"selection\", got {v}")),
|
||||
}
|
||||
let Paint::Gradient(g) = paint else { return Err("span: \"selection\" places a gradient: give `gradient`".into()) };
|
||||
if p.get("swatch").is_some() {
|
||||
return Err("span: \"selection\" places a `gradient`, not a swatch".into());
|
||||
}
|
||||
if g.gradient.kind == vectorcraft_color::GradientKind::Freeform || g.freeform.is_some() {
|
||||
return Err("span: \"selection\" places a linear or radial gradient, not a freeform one".into());
|
||||
}
|
||||
if g.geom.is_some() {
|
||||
return Err(
|
||||
"span: \"selection\" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates)".into()
|
||||
);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// `paint` placed once on the union of the bounds of `ids` (each one's fill box, or the box its
|
||||
/// stroke paints), at the gradient's angle and aspect: every target gets the same vector in
|
||||
/// document coordinates, so the ramp runs on across them (#2127).
|
||||
fn placed_across(d: &Document, ids: &[NodeId], paint: Paint, p: &Value, fill: bool) -> Paint {
|
||||
let union = ids
|
||||
.iter()
|
||||
.filter_map(|id| d.node(*id))
|
||||
.filter_map(|n| item_paint_bounds(n, None, fill).or_else(|| n.geometric_bounds()))
|
||||
.filter(|b| [b.x0, b.y0, b.x1, b.y1].iter().all(|v| v.is_finite()))
|
||||
.reduce(|a, b| a.union(b));
|
||||
let (Some(union), Paint::Gradient(g)) = (union, &paint) else { return paint };
|
||||
// An `aspect` places a radial gradient on the box already; otherwise fit it there.
|
||||
match place_paint(&paint, p, Some(union)) {
|
||||
Paint::Gradient(placed) if placed.geom.is_some() => Paint::Gradient(placed),
|
||||
_ => {
|
||||
let geom = vectorcraft_color::GradientGeom::fit(g.gradient.kind, union, g.angle);
|
||||
Paint::Gradient(Box::new(GradientPaint { geom: Some(geom), ..(**g).clone() }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The mesh point the active tool has focused (its `meshPoint` option: the Mesh tool's or Direct
|
||||
/// Selection's point last clicked).
|
||||
fn focused_mesh_point(s: &Session) -> Option<(NodeId, usize)> {
|
||||
|
||||
@@ -1894,6 +1894,8 @@ mod tests_gradient;
|
||||
#[cfg(test)]
|
||||
mod tests_gradpanel;
|
||||
#[cfg(test)]
|
||||
mod tests_gradspan;
|
||||
#[cfg(test)]
|
||||
mod tests_halftone;
|
||||
#[cfg(test)]
|
||||
mod tests_history_mem;
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
//! `paint.setFill` / `paint.setStroke` `span: "selection"`: one gradient placed on the union of
|
||||
//! the targets' bounds, so the ramp runs across them (#2127).
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_color::{GradientPaint, Paint};
|
||||
use vectorcraft_geom::{Affine, Point};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn session() -> Session {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 400, "height": 200})).unwrap();
|
||||
s
|
||||
}
|
||||
|
||||
fn rect(s: &mut Session, x: f64, y: f64, w: f64, h: f64) -> NodeId {
|
||||
let r = s.execute("shape.rectangle", &json!({"x": x, "y": y, "width": w, "height": h})).unwrap();
|
||||
NodeId(r["id"].as_u64().unwrap())
|
||||
}
|
||||
|
||||
fn fill_gradient(s: &Session, id: NodeId) -> GradientPaint {
|
||||
match s.doc().unwrap().doc.node(id).unwrap().appearance.fill_paint() {
|
||||
Paint::Gradient(g) => *g,
|
||||
p => panic!("expected a gradient fill, got {p:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn stroke_gradient(s: &Session, id: NodeId) -> GradientPaint {
|
||||
match &s.doc().unwrap().doc.node(id).unwrap().appearance.stroke().unwrap().paint {
|
||||
Paint::Gradient(g) => (**g).clone(),
|
||||
p => panic!("expected a gradient stroke, got {p:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn near(a: Point, b: Point) -> bool {
|
||||
(a - b).hypot() < 1e-6
|
||||
}
|
||||
|
||||
fn black_to_white() -> Value {
|
||||
json!({"kind": "linear", "stops": [{"offset": 0, "color": "#000000"}, {"offset": 1, "color": "#ffffff"}]})
|
||||
}
|
||||
|
||||
/// The red channel of the rendered document at (x, y).
|
||||
fn red_at(s: &Session, x: u32, y: u32) -> u8 {
|
||||
let doc = s.doc().unwrap().doc.clone();
|
||||
let img = vectorcraft_render::Renderer::new().render(&doc, 400, 200, Affine::IDENTITY, &Default::default());
|
||||
img.pixel(x, y)[0]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_linear_gradient_spans_the_union_of_the_targets() {
|
||||
let mut s = session();
|
||||
let a = rect(&mut s, 0.0, 50.0, 100.0, 100.0);
|
||||
let b = rect(&mut s, 100.0, 0.0, 100.0, 200.0);
|
||||
let c = rect(&mut s, 300.0, 50.0, 100.0, 100.0);
|
||||
s.execute("paint.setFill", &json!({"ids": [a.0, b.0, c.0], "gradient": black_to_white(), "span": "selection"})).unwrap();
|
||||
// One vector for all three, across the union 0..400 × 0..200 at angle 0.
|
||||
for id in [a, b, c] {
|
||||
let g = fill_gradient(&s, id);
|
||||
let geom = g.geom.expect("placed in document coordinates");
|
||||
assert!(near(geom.start, Point::new(0.0, 100.0)) && near(geom.end, Point::new(400.0, 100.0)), "{id:?}: {geom:?}");
|
||||
}
|
||||
// The ramp runs on across the objects: dark on the left one, light on the right one, and the
|
||||
// edge the first two share is the same colour on both sides.
|
||||
let (left, right) = (red_at(&s, 5, 100), red_at(&s, 395, 100));
|
||||
assert!(left < 20 && right > 235, "{left} {right}");
|
||||
let (edge_a, edge_b) = (i32::from(red_at(&s, 98, 100)), i32::from(red_at(&s, 102, 100)));
|
||||
assert!((edge_a - edge_b).abs() <= 4 && (50..80).contains(&edge_a), "{edge_a} {edge_b}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn without_span_each_object_fits_its_own_box() {
|
||||
let mut s = session();
|
||||
let a = rect(&mut s, 0.0, 50.0, 100.0, 100.0);
|
||||
let b = rect(&mut s, 100.0, 50.0, 100.0, 100.0);
|
||||
for span in [json!(null), json!("object")] {
|
||||
let mut p = json!({"ids": [a.0, b.0], "gradient": black_to_white()});
|
||||
if !span.is_null() {
|
||||
p["span"] = span;
|
||||
}
|
||||
s.execute("paint.setFill", &p).unwrap();
|
||||
assert_eq!(fill_gradient(&s, a).geom, None);
|
||||
// Each ramp starts dark again on its own object.
|
||||
assert!(red_at(&s, 102, 100) < 20, "{p}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_selection_is_the_default_target_and_angle_and_radial_are_kept() {
|
||||
let mut s = session();
|
||||
let a = rect(&mut s, 0.0, 0.0, 100.0, 100.0);
|
||||
let b = rect(&mut s, 200.0, 100.0, 100.0, 100.0);
|
||||
s.execute("select.set", &json!({"ids": [a.0, b.0]})).unwrap();
|
||||
let mut g = black_to_white();
|
||||
g["angle"] = json!(-90);
|
||||
s.execute("paint.setFill", &json!({"gradient": g, "span": "selection"})).unwrap();
|
||||
// Vertical, top to bottom, over the union 0..300 × 0..200.
|
||||
let geom = fill_gradient(&s, b).geom.unwrap();
|
||||
assert!(near(geom.start, Point::new(150.0, 0.0)) && near(geom.end, Point::new(150.0, 200.0)), "{geom:?}");
|
||||
assert_eq!(fill_gradient(&s, a).geom, Some(geom));
|
||||
// Radial: centred on the union, radius half its larger side, aspect kept.
|
||||
s.execute("paint.setFill", &json!({"gradient": {"kind": "radial", "aspect": 50, "stops": black_to_white()["stops"]}, "span": "selection"}))
|
||||
.unwrap();
|
||||
let geom = fill_gradient(&s, a).geom.unwrap();
|
||||
assert!(near(geom.start, Point::new(150.0, 100.0)), "{geom:?}");
|
||||
assert!(((geom.end - geom.start).hypot() - 150.0).abs() < 1e-6, "{geom:?}");
|
||||
assert!((geom.aspect - 0.5).abs() < 1e-9, "{geom:?}");
|
||||
assert_eq!(fill_gradient(&s, b).geom, Some(geom));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strokes_span_the_targets_too() {
|
||||
let mut s = session();
|
||||
let a = rect(&mut s, 0.0, 50.0, 100.0, 100.0);
|
||||
let b = rect(&mut s, 300.0, 50.0, 100.0, 100.0);
|
||||
s.execute("paint.setStroke", &json!({"ids": [a.0, b.0], "gradient": black_to_white(), "span": "selection"})).unwrap();
|
||||
let (ga, gb) = (stroke_gradient(&s, a).geom.unwrap(), stroke_gradient(&s, b).geom.unwrap());
|
||||
assert_eq!(ga, gb);
|
||||
assert!(ga.start.x < 0.0 && ga.end.x > 400.0, "the stroke's box, half its weight out: {ga:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_new_art_default_stays_unplaced() {
|
||||
let mut s = session();
|
||||
let a = rect(&mut s, 0.0, 0.0, 100.0, 100.0);
|
||||
s.execute("paint.setFill", &json!({"ids": [a.0], "gradient": black_to_white(), "span": "selection"})).unwrap();
|
||||
s.execute("select.set", &json!({"ids": []})).unwrap();
|
||||
let n = rect(&mut s, 200.0, 0.0, 50.0, 50.0);
|
||||
assert_eq!(fill_gradient(&s, n).geom, None, "the next object fits the gradient to itself");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_spans_are_refused_and_change_nothing() {
|
||||
let mut s = session();
|
||||
let a = rect(&mut s, 0.0, 0.0, 100.0, 100.0);
|
||||
let b = rect(&mut s, 200.0, 0.0, 100.0, 100.0);
|
||||
let before = s.doc().unwrap().doc.clone();
|
||||
let mut with_vector = black_to_white();
|
||||
with_vector["start"] = json!([0, 0]);
|
||||
with_vector["end"] = json!([10, 0]);
|
||||
for bad in [
|
||||
json!({"ids": [a.0, b.0], "gradient": black_to_white(), "span": "page"}),
|
||||
json!({"ids": [a.0, b.0], "gradient": black_to_white(), "span": true}),
|
||||
json!({"ids": [a.0, b.0], "color": "#ff0000", "span": "selection"}),
|
||||
json!({"ids": [a.0, b.0], "gradient": {"kind": "freeform"}, "span": "selection"}),
|
||||
json!({"ids": [a.0, b.0], "gradient": with_vector, "span": "selection"}),
|
||||
] {
|
||||
let e = s.execute("paint.setFill", &bad).unwrap_err().to_string();
|
||||
assert!(e.contains("span"), "{bad}: {e}");
|
||||
assert_eq!(s.doc().unwrap().doc, before, "{bad}");
|
||||
}
|
||||
}
|
||||
@@ -4869,7 +4869,11 @@ theme Appearance Външен вид
|
||||
|
||||
# Type › Reset Tracking and Kerning
|
||||
Reset Tracking and Kerning Нулиране на разредката и кернинга
|
||||
|
||||
@msg span must be "object" or "selection", got {v} span трябва да е "object" или "selection", а не {v}
|
||||
@msg span: "selection" places a `gradient`, not a swatch span: "selection" поставя `gradient`, а не мостра
|
||||
@msg span: "selection" places a gradient: give `gradient` span: "selection" поставя градиент: задайте `gradient`
|
||||
@msg span: "selection" places a linear or radial gradient, not a freeform one span: "selection" поставя линеен или радиален градиент, а не свободен
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" поставя самия градиент: пропуснете `start` и `end` (те вече го поставят в координатите на документа)
|
||||
# Layers panel filters (Filter Layers Panel Rows) and Color Panel Options
|
||||
By Layer По слой
|
||||
By Object По обект
|
||||
@@ -4882,7 +4886,6 @@ theme Appearance Външен вид
|
||||
Locked Заключени
|
||||
Color Panel Options Опции на панела „Цвят“
|
||||
Filter Layers Panel Rows Филтриране на редовете в панела „Слоеве“
|
||||
|
||||
# View › Hide Template / Show Template
|
||||
Hide Template Скриване на шаблона
|
||||
Show Template Показване на шаблона
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4903,3 +4903,8 @@ mark-style Japanese Japonské
|
||||
@msg Unknown unit Neznámá jednotka
|
||||
# Revolve cap description
|
||||
Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Uzavře kruhové konce otevřených profilů. Boční řezy částečného otočení zůstávají otevřené.
|
||||
@msg span must be "object" or "selection", got {v} span musí být "object" nebo "selection", ne {v}
|
||||
@msg span: "selection" places a `gradient`, not a swatch span: "selection" umísťuje `gradient`, ne vzorník
|
||||
@msg span: "selection" places a gradient: give `gradient` span: "selection" umísťuje přechod: zadejte `gradient`
|
||||
@msg span: "selection" places a linear or radial gradient, not a freeform one span: "selection" umísťuje lineární nebo radiální přechod, ne volný přechod
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" umísťuje samotný přechod: vynechte `start` a `end` (ty ho už umísťují v souřadnicích dokumentu)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4439,3 +4439,8 @@ theme Appearance Erscheinungsbild
|
||||
# Revolve cap description
|
||||
Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Schließt die kreisförmigen Enden offener Profile. Bei einer Teildrehung bleiben die Schnittseiten offen.
|
||||
Distance Abstand
|
||||
@msg span must be "object" or "selection", got {v} span muss "object" oder "selection" sein, nicht {v}
|
||||
@msg span: "selection" places a `gradient`, not a swatch span: "selection" platziert einen `gradient`, kein Farbfeld
|
||||
@msg span: "selection" places a gradient: give `gradient` span: "selection" platziert einen Verlauf: geben Sie `gradient` an
|
||||
@msg span: "selection" places a linear or radial gradient, not a freeform one span: "selection" platziert einen linearen oder radialen Verlauf, keinen Freiform-Verlauf
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" platziert den Verlauf selbst: lassen Sie `start` und `end` weg (sie platzieren ihn bereits in Dokumentkoordinaten)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4845,3 +4845,8 @@ mark-style Japanese Japonesas
|
||||
# Revolve cap description
|
||||
Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Cierra los extremos circulares de los perfiles abiertos. Los giros parciales mantienen abiertos sus lados de corte.
|
||||
Distance Distancia
|
||||
@msg span must be "object" or "selection", got {v} span debe ser "object" o "selection", no {v}
|
||||
@msg span: "selection" places a `gradient`, not a swatch span: "selection" coloca un `gradient`, no una muestra
|
||||
@msg span: "selection" places a gradient: give `gradient` span: "selection" coloca un degradado: indique `gradient`
|
||||
@msg span: "selection" places a linear or radial gradient, not a freeform one span: "selection" coloca un degradado lineal o radial, no uno de forma libre
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" coloca el propio degradado: omita `start` y `end` (ya lo colocan en coordenadas del documento)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4902,3 +4902,8 @@ theme Appearance Apparence
|
||||
@msg Unknown unit Unité inconnue
|
||||
# Revolve cap description
|
||||
Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Ferme les extrémités circulaires des profils ouverts. Les rotations partielles conservent leurs faces de coupe ouvertes.
|
||||
@msg span must be "object" or "selection", got {v} span doit être "object" ou "selection", pas {v}
|
||||
@msg span: "selection" places a `gradient`, not a swatch span: "selection" place un `gradient`, pas une nuance
|
||||
@msg span: "selection" places a gradient: give `gradient` span: "selection" place un dégradé : indiquez `gradient`
|
||||
@msg span: "selection" places a linear or radial gradient, not a freeform one span: "selection" place un dégradé linéaire ou radial, pas un dégradé de forme libre
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" place le dégradé lui-même : omettez `start` et `end` (ils le placent déjà en coordonnées du document)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4870,3 +4870,8 @@ imageTrace Outline Körvonal
|
||||
# Revolve cap description
|
||||
Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Nyitott profilok kör alakú végeinek lezárása. Részleges körforgatás esetén a vágott oldalak nyitva maradnak.
|
||||
Distance Távolság
|
||||
@msg span must be "object" or "selection", got {v} a span értéke "object" vagy "selection" lehet, nem {v}
|
||||
@msg span: "selection" places a `gradient`, not a swatch span: "selection" egy `gradient` értéket helyez el, nem színmintát
|
||||
@msg span: "selection" places a gradient: give `gradient` span: "selection" színátmenetet helyez el: adja meg a `gradient` értéket
|
||||
@msg span: "selection" places a linear or radial gradient, not a freeform one span: "selection" lineáris vagy sugaras színátmenetet helyez el, nem szabad formájút
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" magát a színátmenetet helyezi el: hagyja el a `start` és `end` értéket (ezek már a dokumentum koordinátáiban helyezik el)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4479,3 +4479,8 @@ lighting Light Luce
|
||||
# Revolve cap description
|
||||
Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Chiude le estremità circolari dei profili aperti. Le rotazioni parziali mantengono aperti i lati di taglio.
|
||||
Distance Distanza
|
||||
@msg span must be "object" or "selection", got {v} span deve essere "object" o "selection", non {v}
|
||||
@msg span: "selection" places a `gradient`, not a swatch span: "selection" posiziona un `gradient`, non un campione
|
||||
@msg span: "selection" places a gradient: give `gradient` span: "selection" posiziona una sfumatura: indica `gradient`
|
||||
@msg span: "selection" places a linear or radial gradient, not a freeform one span: "selection" posiziona una sfumatura lineare o radiale, non una sfumatura a forma libera
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" posiziona la sfumatura stessa: ometti `start` e `end` (la posizionano già in coordinate del documento)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4474,3 +4474,8 @@ lighting Light ライト
|
||||
# Revolve cap description
|
||||
Close the circular ends of open profiles. Partial sweeps keep their cut sides open. 開いたプロファイルの円形の端を閉じます。部分的な回転では切断面は開いたままです。
|
||||
Distance 距離
|
||||
@msg span must be "object" or "selection", got {v} span は "object" または "selection" で指定してください ({v} は指定できません)
|
||||
@msg span: "selection" places a `gradient`, not a swatch span: "selection" は `gradient` を配置します。スウォッチは配置できません
|
||||
@msg span: "selection" places a gradient: give `gradient` span: "selection" はグラデーションを配置します: `gradient` を指定してください
|
||||
@msg span: "selection" places a linear or radial gradient, not a freeform one span: "selection" は線形または円形グラデーションを配置します。フリーグラデーションは配置できません
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" はグラデーション自体を配置します: `start` と `end` は省略してください (これらはすでにドキュメント座標で配置を指定します)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4443,3 +4443,8 @@ lighting Light 광원
|
||||
# Revolve cap description
|
||||
Close the circular ends of open profiles. Partial sweeps keep their cut sides open. 열린 프로파일의 원형 끝을 닫습니다. 부분 회전에서는 절단 측면이 열린 상태로 유지됩니다.
|
||||
Distance 거리
|
||||
@msg span must be "object" or "selection", got {v} span은 "object" 또는 "selection"이어야 합니다. {v}은(는) 사용할 수 없습니다
|
||||
@msg span: "selection" places a `gradient`, not a swatch span: "selection"은 `gradient`를 배치합니다. 견본은 배치할 수 없습니다
|
||||
@msg span: "selection" places a gradient: give `gradient` span: "selection"은 그레이디언트를 배치합니다: `gradient`를 지정하세요
|
||||
@msg span: "selection" places a linear or radial gradient, not a freeform one span: "selection"은 선형 또는 방사형 그레이디언트를 배치합니다. 자유형 그레이디언트는 배치할 수 없습니다
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection"은 그레이디언트 자체를 배치합니다: `start`와 `end`를 빼세요 (이미 문서 좌표로 배치합니다)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4746,3 +4746,8 @@ mark-style Japanese Japońskie
|
||||
# Revolve cap description
|
||||
Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Zamknij okrągłe końce otwartych profili. Przy częściowym obrocie boki przekroju pozostają otwarte.
|
||||
Distance Odległość
|
||||
@msg span must be "object" or "selection", got {v} span musi mieć wartość "object" lub "selection", a nie {v}
|
||||
@msg span: "selection" places a `gradient`, not a swatch span: "selection" umieszcza `gradient`, a nie próbkę
|
||||
@msg span: "selection" places a gradient: give `gradient` span: "selection" umieszcza gradient: podaj `gradient`
|
||||
@msg span: "selection" places a linear or radial gradient, not a freeform one span: "selection" umieszcza gradient liniowy lub radialny, a nie dowolny
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" umieszcza sam gradient: pomiń `start` i `end` (one już umieszczają go we współrzędnych dokumentu)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3803,3 +3803,8 @@ axis Both Ambos
|
||||
Update Dimensions Atualizar Cotas
|
||||
# Revolve cap description
|
||||
Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Fecha as extremidades circulares dos perfis abertos. Giros parciais mantêm as faces de corte abertas.
|
||||
@msg span must be "object" or "selection", got {v} span deve ser "object" ou "selection", não {v}
|
||||
@msg span: "selection" places a `gradient`, not a swatch span: "selection" posiciona um `gradient`, não uma amostra
|
||||
@msg span: "selection" places a gradient: give `gradient` span: "selection" posiciona um degradê: informe `gradient`
|
||||
@msg span: "selection" places a linear or radial gradient, not a freeform one span: "selection" posiciona um degradê linear ou radial, não um de forma livre
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" posiciona o próprio degradê: omita `start` e `end` (eles já o posicionam em coordenadas do documento)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -4475,3 +4475,8 @@ lighting Light Свет
|
||||
# Revolve cap description
|
||||
Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Закрывает круглые торцы открытых профилей. При неполном вращении боковые срезы остаются открытыми.
|
||||
Distance Расстояние
|
||||
@msg span must be "object" or "selection", got {v} span должен быть "object" или "selection", а не {v}
|
||||
@msg span: "selection" places a `gradient`, not a swatch span: "selection" размещает `gradient`, а не образец
|
||||
@msg span: "selection" places a gradient: give `gradient` span: "selection" размещает градиент: укажите `gradient`
|
||||
@msg span: "selection" places a linear or radial gradient, not a freeform one span: "selection" размещает линейный или радиальный градиент, а не произвольный
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" сам размещает градиент: уберите `start` и `end` (они уже задают его в координатах документа)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4459,3 +4459,8 @@ lighting Light Світло
|
||||
# Revolve cap description
|
||||
Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Закриває круглі торці відкритих профілів. За неповного обертання бічні зрізи залишаються відкритими.
|
||||
Distance Відстань
|
||||
@msg span must be "object" or "selection", got {v} span має бути "object" або "selection", а не {v}
|
||||
@msg span: "selection" places a `gradient`, not a swatch span: "selection" розміщує `gradient`, а не зразок
|
||||
@msg span: "selection" places a gradient: give `gradient` span: "selection" розміщує градієнт: вкажіть `gradient`
|
||||
@msg span: "selection" places a linear or radial gradient, not a freeform one span: "selection" розміщує лінійний або радіальний градієнт, а не довільний
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" сам розміщує градієнт: приберіть `start` і `end` (вони вже задають його в координатах документа)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 718 and column 40.
|
@@ -4458,3 +4458,8 @@ theme Appearance Giao diện
|
||||
# Revolve cap description
|
||||
Close the circular ends of open profiles. Partial sweeps keep their cut sides open. Đóng các đầu tròn của biên dạng mở. Khi xoay một phần, các mặt cắt bên vẫn mở.
|
||||
Distance Khoảng cách
|
||||
@msg span must be "object" or "selection", got {v} span phải là "object" hoặc "selection", không phải {v}
|
||||
@msg span: "selection" places a `gradient`, not a swatch span: "selection" đặt một `gradient`, không phải mẫu màu
|
||||
@msg span: "selection" places a gradient: give `gradient` span: "selection" đặt một dải chuyển màu: hãy cung cấp `gradient`
|
||||
@msg span: "selection" places a linear or radial gradient, not a freeform one span: "selection" đặt dải chuyển màu tuyến tính hoặc xuyên tâm, không phải dạng tự do
|
||||
@msg span: "selection" places the gradient itself: leave out `start` and `end` (they already place it in document coordinates) span: "selection" tự đặt dải chuyển màu: hãy bỏ `start` và `end` (chúng đã đặt nó theo tọa độ tài liệu)
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 3724 and column 10.
|
@@ -278,6 +278,9 @@ explicit `ids`.
|
||||
|
||||
Paint values (`fill`, `stroke`) accept `"#rrggbb"`, `"none"`, `[r,g,b]` (0..1), `{"c","m","y","k"}`, `{"gray"}`,
|
||||
or a full `paint.setFill` params object (`{"gradient": …}`, `{"swatch": "name"}`).
|
||||
A gradient given to several objects fits each one's own bounds; `paint.setFill` / `paint.setStroke` with
|
||||
`span: "selection"` place a linear or radial gradient once on the union of the targets' bounds instead, so one
|
||||
ramp runs across them.
|
||||
For `draw_shape` and `draw_path`, omitted paint fields inherit the current [new-art appearance](#new-art).
|
||||
For a filled shape or path without an outline, pass an explicit `fill` and `stroke: "none"`.
|
||||
Lines start with no fill and a visible stroke.
|
||||
|
||||
Reference in New Issue
Block a user