A text's path becomes construction (#1119)
sketch.text and sketch.edit_text make the curve a text follows construction in the same undo step, so it no longer cuts the letters into pieces; the reply names the curve as guide.
This commit is contained in:
1 parent
ed943a74e6
commit
6ebfcd16b6
3 files changed
+63
-3
No files matched your search
@@ -8,7 +8,7 @@ use solidcraft_sketch::{ConstraintKind, CurveKind, LinkKind, LinkSource, Sketch,
|
||||
/// The style parameters of `sketch.text` and `sketch.edit_text`.
|
||||
macro_rules! text_style_doc {
|
||||
() => {
|
||||
"bold?, italic?: bool (an installed family's bold or italic face; italic without one is slanted), align?: left|center|right (the lines against `at`, or along the path), letter_spacing?: extra mm between letters, line_spacing?: times the font's line distance (default 1), path?: a curve id of the sketch the baseline follows (\"\": none; `at` and `angle` then do not place the text), reverse?: bool (the other way along the path)"
|
||||
"bold?, italic?: bool (an installed family's bold or italic face; italic without one is slanted), align?: left|center|right (the lines against `at`, or along the path), letter_spacing?: extra mm between letters, line_spacing?: times the font's line distance (default 1), path?: a curve id of the sketch the baseline follows (\"\": none; `at` and `angle` then do not place the text; the curve becomes construction, a guide the letters stand on), reverse?: bool (the other way along the path)"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -209,11 +209,28 @@ fn text(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if geom.is_empty() {
|
||||
return Err(bad(cmd, "the text makes no outline"));
|
||||
}
|
||||
let guide = guide(&mut sk, &src);
|
||||
let l = sk.add_link(LinkKind::Text, src, &geom)?;
|
||||
let curves = ids_of(&sk, &sk.link_curves(&l));
|
||||
*doc.sketch_mut(id)? = sk;
|
||||
*s.doc_mut() = doc;
|
||||
Ok(json!({"link": l, "curves": curves}))
|
||||
Ok(json!({"link": l, "curves": curves, "guide": guide}))
|
||||
}
|
||||
|
||||
/// A text's path is its guide: the curve it follows becomes construction. Left as it was, it
|
||||
/// would cut the letters standing on it (and the ones hanging below it) into pieces, each
|
||||
/// profile a piece of a letter and of the curve, and picking the text would pick those pieces.
|
||||
/// Taking the text off the curve leaves the curve as it is. The curve made construction, if any.
|
||||
fn guide(sk: &mut Sketch, src: &LinkSource) -> Option<String> {
|
||||
let LinkSource::Text { style, .. } = src else { return None };
|
||||
let id = style.path.as_ref()?;
|
||||
let curve = sk.curve_index(id).and_then(|c| sk.curves.get_mut(c))?;
|
||||
if curve.construction {
|
||||
return None;
|
||||
}
|
||||
curve.construction = true;
|
||||
curve.centerline = false;
|
||||
Some(id.clone())
|
||||
}
|
||||
|
||||
/// The text source owning an outline or one of its linked points.
|
||||
@@ -290,10 +307,11 @@ fn apply_text_edit(sk: &mut Sketch, p: &Value) -> Result<Value> {
|
||||
if geom.is_empty() {
|
||||
return Err(bad(cmd, "the text makes no outline"));
|
||||
}
|
||||
let guide = guide(sk, &src);
|
||||
sk.set_link_source(&l, src.clone());
|
||||
sk.update_link(&l, &geom)?;
|
||||
let curves = ids_of(sk, &sk.link_curves(&l));
|
||||
Ok(json!({"link": l, "curves": curves}))
|
||||
Ok(json!({"link": l, "curves": curves, "guide": guide}))
|
||||
}
|
||||
|
||||
/// The curve ending at point `pi` and the direction leaving it there.
|
||||
|
||||
@@ -333,3 +333,40 @@ fn sketch_text_inputs_left_out_or_null_keep_their_defaults() {
|
||||
assert_eq!((text.as_str(), height, angle), ("II", 20.0, 0.0));
|
||||
assert!(at.dist(Vec2::new(3.0, 4.0)) < 1e-12, "{at:?}");
|
||||
}
|
||||
|
||||
/// A text set on a spline stands on it, its descenders below it: the spline becomes
|
||||
/// construction, so it doesn't cut the letters into pieces. Every profile is then the text's
|
||||
/// alone (picking the text picks letters, not pieces of letters and curve), and one undo puts
|
||||
/// the text and the curve back as they were.
|
||||
#[test]
|
||||
fn a_text_on_a_spline_turns_its_path_into_a_guide() {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "sketch.create", json!({"plane":"XY"}));
|
||||
let spline = run(&mut s, "sketch.spline.fit_point", json!({"points":[[0,0],[30,15],[60,0],[90,10]]}))["created"]["curves"][0]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
let construction = |s: &Session| {
|
||||
let sk = s.doc.sketch(s.active_sketch.unwrap()).unwrap();
|
||||
sk.curves.iter().find(|c| c.id == spline).unwrap().construction
|
||||
};
|
||||
let t = run(&mut s, "sketch.text", json!({"text":"Phago","at":[0,0],"height":10,"path":spline}));
|
||||
assert_eq!(t["guide"], json!(spline));
|
||||
assert!(construction(&s));
|
||||
let state = s.world_state();
|
||||
let ss = state.sketch(s.active_sketch.unwrap()).unwrap();
|
||||
for (i, p) in ss.profiles.iter().enumerate() {
|
||||
let alone = p.curve_ids().iter().all(|id| ss.sketch.curves.iter().any(|c| &c.id == id && c.link.is_some()));
|
||||
assert!(alone, "profile {i} is bounded by the spline too");
|
||||
}
|
||||
// Taken off the curve, the text leaves it a guide; set on it again, nothing changes.
|
||||
let link = t["link"].as_str().unwrap().to_string();
|
||||
run(&mut s, "sketch.edit_text", json!({"link":link,"path":""}));
|
||||
assert!(construction(&s));
|
||||
assert_eq!(run(&mut s, "sketch.edit_text", json!({"link":link,"path":spline}))["guide"], json!(null));
|
||||
// One undo for each step, back to the text's first state, then to before it.
|
||||
for _ in 0..3 {
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
}
|
||||
assert!(!construction(&s), "undoing the text gives the curve back");
|
||||
}
|
||||
@@ -57,6 +57,11 @@ geometry cannot identify one text: use **Select Other** to choose its outline in
|
||||
Change the text, font, baseline X/Y, height or angle, then confirm with **OK**; **Cancel**
|
||||
keeps the original text. Position and height use the document's units.
|
||||
|
||||
A text can follow a curve of its sketch (its **Path**). That curve becomes construction: the
|
||||
letters stand on it and their descenders cross it, so as a normal curve it would cut them into
|
||||
pieces, and picking the text would pick those pieces. Taking the text off the curve leaves it
|
||||
construction; **Normal/Construction** turns it back.
|
||||
|
||||
Drag a text outline to move all its letters together. Let go to finish in one undo step,
|
||||
or press **Esc** during the drag to restore the original text. The text stays editable.
|
||||
For an exact displacement, select an outline and choose **Move/Copy** from its context menu.
|
||||
|
||||
Reference in new issue
Block a user