feat(draw): Arc Continue command; 3-Point default

Arc ribbon default + icon + dropdown order set to 3-Point. New ARC_CONT: tangent-continuing arc from the last line/arc endpoint, pick end only; Ctrl flips direction. Exit anchor recorded on commit (disambiguated by last pick) so chaining continues from the correct end.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-08 20:39:40 +03:00
1 parent b808fc2f86
commit c4fcfacb18
10 files changed
+212 -13

No files matched your search

+5
View File
@@ -11,10 +11,12 @@ impl OpenCADStudio {
/// lives in exactly one place. No-op when no command is active.
pub(super) fn feed_command(&mut self, input: StepInput) -> Task<Message> {
let i = self.active_tab;
let ctrl = self.ctrl_down;
let result: Option<CmdResult> = {
let Some(cmd) = self.tabs[i].active_cmd.as_mut() else {
return Task::none();
};
cmd.set_ctrl(ctrl);
match input {
StepInput::Point(p) => Some(cmd.on_point(p)),
StepInput::Text(s) => cmd.on_text_input(&s),
@@ -284,6 +286,9 @@ impl OpenCADStudio {
&path,
);
}
// Record where this draw ended so ARC_CONT can continue from it
// (before `entity` is moved into commit_entity).
self.update_cont_anchor(&entity);
let label = self.history_label_from_active_cmd(i, "ENTITY");
self.push_undo_snapshot(i, label);
self.commit_entity(entity);
+25
View File
@@ -292,6 +292,31 @@ impl OpenCADStudio {
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"ARC_CONT" => {
use crate::modules::draw::draw::arc::{continue_anchor, ArcContCommand};
// Prefer the anchor recorded when the last line/arc was drawn (it
// knows the true drawing-end tangent); otherwise fall back to the
// last line/arc found in the document (e.g. after a file load).
let seed = self.cont_anchor.or_else(|| {
self.tabs[i]
.scene
.document
.entities()
.filter_map(|e| continue_anchor(e, None))
.last()
});
match seed {
Some((s, tangent)) => {
let new_cmd = ArcContCommand::new(s, tangent);
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
None => {
self.command_line
.push_info("ARC Continue No previous line or arc to continue.");
}
}
}
"RECT" | "RECTANG" | "REC" => {
use crate::modules::draw::draw::shapes::RectCommand;
+5
View File
@@ -392,6 +392,10 @@ pub(super) struct OpenCADStudio {
show_layout_tabs: bool,
/// Last point committed by a drawing command — used as ortho/polar base.
last_point: Option<glam::Vec3>,
/// Endpoint + unit exit-tangent of the most recently drawn line/arc, so
/// `ARC_CONT` (Arc → Continue) can start tangentially from where drawing
/// ended. `None` once a non-line/arc entity is committed.
cont_anchor: Option<(glam::DVec3, glam::DVec3)>,
/// OS window Id for the floating Layer Properties Manager (None when closed).
/// OS window Id of the primary application window.
main_window: Option<window::Id>,
@@ -2150,6 +2154,7 @@ impl OpenCADStudio {
clipboard_deps: ClipboardDeps::default(),
shift_down: false,
ctrl_down: false,
cont_anchor: None,
mtext_editor: None,
text_inline: None,
layout_context_menu: None,
+20
View File
@@ -198,6 +198,26 @@ impl OpenCADStudio {
}
}
/// Push the live Ctrl state into tab `i`'s active command before it builds a
/// preview/commit (arc-direction flip on `ARC_CONT`, etc.).
pub(in crate::app) fn push_ctrl_to_cmd(&mut self, i: usize) {
let ctrl = self.ctrl_down;
if let Some(c) = self.tabs[i].active_cmd.as_mut() {
c.set_ctrl(ctrl);
}
}
/// Cache the endpoint + exit tangent of a just-committed line/arc so a
/// following `ARC_CONT` starts tangentially from where drawing ended. Set to
/// `None` for any other entity kind. Uses `last_point` (the final pick) to
/// pick the endpoint the pen actually finished on.
pub(in crate::app) fn update_cont_anchor(&mut self, entity: &acadrust::EntityType) {
let last = self
.last_point
.map(|v| glam::DVec3::new(v.x as f64, v.y as f64, v.z as f64));
self.cont_anchor = crate::modules::draw::draw::arc::continue_anchor(entity, last);
}
pub(in crate::app) fn dyn_resolve_point(&self) -> Option<glam::Vec3> {
use crate::app::document::DynComponent;
let i = self.active_tab;
+9
View File
@@ -1751,8 +1751,17 @@ impl OpenCADStudio {
}
Message::SetModifiers { shift, ctrl } => {
let ctrl_changed = self.ctrl_down != ctrl;
self.shift_down = shift;
self.ctrl_down = ctrl;
// A live command may key its preview off Ctrl (arc-direction
// flip). Rebuild it at the current cursor so the flip shows
// without waiting for the next mouse move.
let i = self.active_tab;
if ctrl_changed && self.tabs[i].active_cmd.is_some() {
let p = self.tabs[i].last_cursor_screen;
return Task::done(Message::ViewportMove(p));
}
Task::none()
}
+4 -2
View File
@@ -1090,9 +1090,11 @@ pub(super) fn on_tick(&mut self, t: Instant) -> Task<Message> {
s.screen.y += tile_b.y;
}
// Give the command the current UCS before it builds its
// rubber-band preview (and, by persistence, its commit).
// Give the command the current UCS + Ctrl state before it
// builds its rubber-band preview (and, by persistence, its
// commit).
self.push_ucs_to_cmd(i);
self.push_ctrl_to_cmd(i);
let mut previews = if needs_structure {
let mut p = self.tabs[i]
.active_cmd