Files
cadcraft/crates/engine/src/cmd/modify2/blend.rs
T
2ez4gcxandClaude Opus 5.5 990ff2ce9f Say "or" once in prompts that offer options (#2563)
* Say "or" once in prompts that offer options

Twelve prompt messages ended in "or", and the command line adds "or"
before a prompt's options, so they read "or or": HELIX height, XLINE angle
and offset, BLEND, four LENGTHEN prompts, two NCOPY prompts, PEDIT and
table breaks. They now read as AutoCAD's do.

The interface catalogs are keyed by these messages: each key loses its
"or" and each translation its trailing word for "or"; where the shorter
key already had a translation, the longer row is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Say "or" once in HELIX's radius prompts; rename the Vietnamese keys

HELIX's base and top radius prompts ended in "or" before their Diameter
option too, so they read "Specify base radius or or [Diameter]". They
now read "Specify base radius or [Diameter] <1.0000>:", and their keys
lose "or" in all 17 catalogs, as the other twelve did.

The Vietnamese catalog, merged from main, gets the same renames as the
other catalogs; where the shorter key already had a translation, the
longer row goes. The unused "Specify rotation angle or" row goes from
every catalog too: RECTANG has asked "Specify rotation angle" since
#2554.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 14:36:37 -07:00

121 lines
4.4 KiB
Rust

use super::{
Accept, Chain, EntityKind, Handle, Input, Interactive, Prompt, Result, Session, Spline, Step, Value, Vec2, bad, curves, json, other, point_req,
str_param,
};
// =====================================================================================
// BLEND
// =====================================================================================
/// Endpoint of a curve nearest `pick` and the outward unit tangent there.
fn curve_end(k: &EntityKind, pick: Vec2) -> Option<(Vec2, Vec2)> {
let c = Chain::of(k)?;
if c.closed {
return None;
}
let (a, b) = (c.start()?, c.end()?);
if pick.dist(b) <= pick.dist(a) {
let t = c.segs.last()?.tangent(1.0);
Some((b, t))
} else {
let t = c.segs.first()?.tangent(0.0);
Some((a, -t))
}
}
pub(crate) fn blend_spline(e1: (Vec2, Vec2), e2: (Vec2, Vec2), smooth: bool) -> Option<Spline> {
let (p1, t1) = e1;
let (p2, t2) = e2;
let d = p1.dist(p2);
if d < 1e-12 {
return None;
}
let ctrl = if smooth {
let k = d / 5.0;
vec![p1, p1 + t1 * k, p1 + t1 * (2.0 * k), p2 + t2 * (2.0 * k), p2 + t2 * k, p2]
} else {
let k = d / 3.0;
vec![p1, p1 + t1 * k, p2 + t2 * k, p2]
};
let deg = ctrl.len() - 1;
Some(Spline::from_control(ctrl, deg))
}
fn blend(s: &mut Session, h1: Handle, p1: Vec2, h2: Handle, p2: Vec2, smooth: bool) -> Result<Handle> {
let k1 = curves::entity(s, h1)?.kind;
let k2 = curves::entity(s, h2)?.kind;
let e1 = curve_end(&k1, p1).ok_or_else(|| other("Select an open line, arc, polyline, ellipse arc or spline."))?;
let e2 = curve_end(&k2, p2).ok_or_else(|| other("Select an open line, arc, polyline, ellipse arc or spline."))?;
let sp = blend_spline(e1, e2, smooth).ok_or_else(|| other("The ends coincide."))?;
s.add_entity(EntityKind::Spline(sp))
}
pub(super) fn run_blend(s: &mut Session, p: &Value) -> Result<Value> {
let h1 = curves::handle_param(p, "h1").ok_or_else(|| bad("blend", "`h1` is required"))?;
let h2 = curves::handle_param(p, "h2").ok_or_else(|| bad("blend", "`h2` is required"))?;
let p1 = point_req("blend", p, "p1")?;
let p2 = point_req("blend", p, "p2")?;
let smooth = str_param(p, "continuity").is_some_and(|c| c.eq_ignore_ascii_case("smooth"));
let h = blend(s, h1, p1, h2, p2, smooth)?;
Ok(json!({ "handle": h.hex() }))
}
#[derive(Default)]
pub(super) struct BlendM {
first: Option<(Handle, Vec2)>,
smooth: bool,
asking: bool,
}
impl Interactive for BlendM {
fn name(&self) -> &'static str {
"BLEND"
}
fn begin(&mut self, s: &mut Session) -> Result<Step> {
s.set_selection(Vec::new());
s.echo(format!("Continuity = {}", if self.smooth { "Smooth" } else { "Tangent" }));
Ok(Step::Continue)
}
fn prompt(&self, _s: &Session) -> Prompt {
if self.asking {
return Prompt::new("Enter continuity", curves::KW).kw(&["Tangent", "Smooth"]).default("Tangent");
}
match self.first {
None => Prompt::new("Select first object", Accept::POINT).kw(&["CONtinuity"]),
Some(_) => Prompt::new("Select second point", Accept::POINT),
}
}
fn input(&mut self, s: &mut Session, i: Input) -> Result<Step> {
if self.asking {
if let Input::Keyword(k) | Input::Text(k) = &i {
self.smooth = k.to_ascii_lowercase().starts_with('s');
}
self.asking = false;
return Ok(Step::Continue);
}
match i {
Input::Keyword(_) => {
self.asking = true;
Ok(Step::Continue)
}
Input::Point(p) => {
let h = curves::pick_at(s, p).ok_or_else(|| other("*Invalid selection*"))?;
match self.first {
None => {
let k = curves::entity(s, h)?.kind;
curve_end(&k, p).ok_or_else(|| other("Select an open curve."))?;
self.first = Some((h, p));
Ok(Step::Continue)
}
Some((h1, p1)) => {
blend(s, h1, p1, h, p, self.smooth)?;
Ok(Step::Done)
}
}
}
Input::Enter => Ok(Step::Cancel),
_ => Ok(Step::Continue),
}
}
}