Files
OneCAD-Draft/src/modules/draw/draw/ray.rs
T
Hakan SevenandClaude Opus 4.8 4f7a98a2ad feat(f64): command layer compiles on DVec3
Whole command/snap/grip/cursor chain now builds on f64 DVec3 end to end,
so picked points, snaps and grip drags stay precise at UTM-scale
coordinates instead of quantizing to ~0.5 m through f32.

- All ~80 command files migrated: draw (line/circle/arc/ellipse/shapes
  in full f64; others via point methods), modify, annotate, insert, view,
  inquiry, layers, groups, clipboard, layout.
- Entity apply_grip impls updated for GripApply::{Absolute,Translate}(DVec3).
- Construction sites (EntityTransform/CmdResult/DynAnchor) emit DVec3;
  wire-preview boundaries cast to f32 only at the GPU edge.
- Modify/annotate commands keep internal Vec3 geometry where precision is
  non-critical, casting at the DVec3 boundary; key draw commands store f64.

Builds clean (no warnings). Visual verification of crosshair/snap/grip at
extreme UTM zoom pending.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 03:51:50 +03:00

185 lines
5.8 KiB
Rust

// Ray / XLine draw commands.
//
// RAY — semi-infinite line: click base point, then click direction point.
// Produces a Ray entity; repeats until Enter/Esc.
// XLINE — infinite construction line: same two-click pattern, yields XLine.
use acadrust::entities::{Ray as RayEnt, XLine as XLineEnt};
use acadrust::types::Vector3;
use acadrust::EntityType;
use crate::command::{CadCommand, CmdResult};
use crate::scene::model::wire_model::WireModel;
use glam::{DVec3, Vec3};
const DISPLAY_EXTENT: f32 = 1_000_000.0;
// ── RAY ───────────────────────────────────────────────────────────────────
pub struct RayCommand {
base: Option<Vec3>,
}
impl RayCommand {
pub fn new() -> Self {
Self { base: None }
}
}
impl CadCommand for RayCommand {
fn name(&self) -> &'static str {
"RAY"
}
fn prompt(&self) -> String {
if self.base.is_none() {
"RAY Specify start point:".into()
} else {
"RAY Specify through point [Enter/Esc = done]:".into()
}
}
fn on_point(&mut self, pt: DVec3) -> CmdResult { let pt = pt.as_vec3();
if let Some(base) = self.base {
let dir = pt - base;
let len = dir.length();
if len < 1e-6 {
return CmdResult::NeedPoint;
}
let dir_n = dir / len;
let ray = RayEnt::new(
Vector3::new(base.x as f64, base.y as f64, base.z as f64),
Vector3::new(dir_n.x as f64, dir_n.y as f64, dir_n.z as f64),
);
// Stay active: new base = same base (can keep clicking through points)
// Actually AutoCAD prompts for new start after each ray — reset base.
self.base = None;
CmdResult::CommitEntity(EntityType::Ray(ray))
} else {
self.base = Some(pt);
CmdResult::NeedPoint
}
}
fn on_enter(&mut self) -> CmdResult {
CmdResult::Cancel
}
fn on_escape(&mut self) -> CmdResult {
CmdResult::Cancel
}
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> { let pt = pt.as_vec3();
let base = self.base?;
let dir = (pt - base).normalize_or_zero();
let far = base + dir * DISPLAY_EXTENT;
Some(WireModel {
name: "ray_preview".into(),
points: vec![[base.x, base.y, base.z], [far.x, far.y, far.z]],
points_low: Vec::new(),
color: WireModel::CYAN,
selected: false,
pattern_length: 0.0,
pattern: [0.0; 8],
line_weight_px: 1.0,
snap_pts: vec![],
tangent_geoms: vec![],
aci: 0,
key_vertices: vec![],
aabb: WireModel::UNBOUNDED_AABB,
plinegen: true,
vp_scissor: None,
fill_tris: vec![],
fill_tris_low: Vec::new(),
})
}
}
// ── XLINE ─────────────────────────────────────────────────────────────────
pub struct XLineCommand {
base: Option<Vec3>,
}
impl XLineCommand {
pub fn new() -> Self {
Self { base: None }
}
}
impl CadCommand for XLineCommand {
fn name(&self) -> &'static str {
"XLINE"
}
fn prompt(&self) -> String {
if self.base.is_none() {
"XLINE Specify a point:".into()
} else {
"XLINE Specify through point [Enter/Esc = done]:".into()
}
}
fn on_point(&mut self, pt: DVec3) -> CmdResult { let pt = pt.as_vec3();
if let Some(base) = self.base {
let dir = pt - base;
let len = dir.length();
if len < 1e-6 {
return CmdResult::NeedPoint;
}
let dir_n = dir / len;
let xline = XLineEnt::new(
Vector3::new(base.x as f64, base.y as f64, base.z as f64),
Vector3::new(dir_n.x as f64, dir_n.y as f64, dir_n.z as f64),
);
self.base = None;
CmdResult::CommitEntity(EntityType::XLine(xline))
} else {
self.base = Some(pt);
CmdResult::NeedPoint
}
}
fn on_enter(&mut self) -> CmdResult {
CmdResult::Cancel
}
fn on_escape(&mut self) -> CmdResult {
CmdResult::Cancel
}
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> { let pt = pt.as_vec3();
let base = self.base?;
let dir = (pt - base).normalize_or_zero();
let far_pos = base + dir * DISPLAY_EXTENT;
let far_neg = base - dir * DISPLAY_EXTENT;
Some(WireModel {
name: "xline_preview".into(),
points: vec![
[far_neg.x, far_neg.y, far_neg.z],
[far_pos.x, far_pos.y, far_pos.z],
],
points_low: Vec::new(),
color: WireModel::CYAN,
selected: false,
pattern_length: 0.0,
pattern: [0.0; 8],
line_weight_px: 1.0,
snap_pts: vec![],
tangent_geoms: vec![],
aci: 0,
key_vertices: vec![],
aabb: WireModel::UNBOUNDED_AABB,
plinegen: true,
vp_scissor: None,
fill_tris: vec![],
fill_tris_low: Vec::new(),
})
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["RAY"] }); // RayCommand
inventory::submit!(crate::command::CommandRegistration { names: &["CONSTRUCTIONLINE", "XL", "XLINE"] }); // XLineCommand