mirror of
https://github.com/storytold/cadcraft.git
synced 2026-11-03 17:18:01 -06:00
* 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>
121 lines
4.4 KiB
Rust
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),
|
|
}
|
|
}
|
|
}
|