style: apply cargo fmt across workspace

This commit is contained in:
Karim Jerbi committed 2026-10-11 03:14:29 +01:00
1 parent 85710e5454
commit 9cf9ff2c5c
531 files changed
+57740 -33906

No files matched your search

+52 -45
View File
@@ -1,8 +1,8 @@
use codec::entities::Solid3D;
use codec::objects::SolidHistoryOperation;
use codec::EntityType;
use kernel::brep::Body;
use glam::DVec3;
use kernel::brep::Body;
use crate::command::{
CadCommand, CmdOption, CmdResult, DynAnchor, DynFieldSpec, DynGuide, DynRole, DynSpec,
@@ -29,7 +29,10 @@ enum Step {
EllipseSecond(DVec3),
EllipseThird(DVec3, DVec3),
TtrFirst,
TtrSecond { object: TangentObject, hit: DVec3 },
TtrSecond {
object: TangentObject,
hit: DVec3,
},
TtrRadius {
first: TangentObject,
second: TangentObject,
@@ -60,8 +63,7 @@ impl Default for Defaults {
}
fn defaults() -> &'static std::sync::Mutex<Defaults> {
static DEFAULTS: std::sync::OnceLock<std::sync::Mutex<Defaults>> =
std::sync::OnceLock::new();
static DEFAULTS: std::sync::OnceLock<std::sync::Mutex<Defaults>> = std::sync::OnceLock::new();
DEFAULTS.get_or_init(|| std::sync::Mutex::new(Defaults::default()))
}
@@ -76,10 +78,7 @@ pub struct CylinderCommand {
impl CylinderCommand {
pub fn new() -> Self {
let remembered = defaults()
.lock()
.map(|value| *value)
.unwrap_or_default();
let remembered = defaults().lock().map(|value| *value).unwrap_or_default();
Self {
step: Step::BaseCenter,
frame: None,
@@ -249,12 +248,7 @@ impl CylinderCommand {
return None;
};
let local = crate::modules::draw::draw::circle::pick_best_ttr_candidate(
first,
second,
radius,
first_hit,
second_hit,
None,
first, second, radius, first_hit, second_hit, None,
)?;
Some(self.plane.to_world(local))
}
@@ -375,10 +369,7 @@ impl CylinderCommand {
})
}
Step::EllipseCenterFirstAxis(center) => (number > 0.0).then(|| {
self.step = Step::EllipseCenterSecondAxis(
center,
center + self.plane.x * number,
);
self.step = Step::EllipseCenterSecondAxis(center, center + self.plane.x * number);
CmdResult::NeedPoint
}),
Step::TtrRadius { .. } => {
@@ -488,48 +479,64 @@ impl CadCommand for CylinderCommand {
fn prompt(&self) -> String {
match self.step {
Step::BaseCenter =>
t!("CYLINDER Specify center point of base or [3P/2P/Ttr/Elliptical]:").into_owned(),
Step::BaseCenter => {
t!("CYLINDER Specify center point of base or [3P/2P/Ttr/Elliptical]:").into_owned()
}
Step::BaseRadius => crate::tf!(
"CYLINDER Specify base radius or [Diameter] <{:.4}>:",
self.remembered.major_radius
).into_owned(),
)
.into_owned(),
Step::BaseDiameter => crate::tf!(
"CYLINDER Specify base diameter <{:.4}>:",
self.remembered.major_radius * 2.0
).into_owned(),
)
.into_owned(),
Step::ThreePointFirst => t!("CYLINDER Specify first point on base:").into_owned(),
Step::ThreePointSecond(_) => t!("CYLINDER Specify second point on base:").into_owned(),
Step::ThreePointThird(_, _) => t!("CYLINDER Specify third point on base:").into_owned(),
Step::TwoPointFirst => t!("CYLINDER Specify first endpoint of base diameter:").into_owned(),
Step::TwoPointSecond(_) => t!("CYLINDER Specify second endpoint of base diameter:").into_owned(),
Step::EllipseFirst =>
t!("CYLINDER Specify endpoint of first axis or [Center]:").into_owned(),
Step::ThreePointThird(_, _) => {
t!("CYLINDER Specify third point on base:").into_owned()
}
Step::TwoPointFirst => {
t!("CYLINDER Specify first endpoint of base diameter:").into_owned()
}
Step::TwoPointSecond(_) => {
t!("CYLINDER Specify second endpoint of base diameter:").into_owned()
}
Step::EllipseFirst => {
t!("CYLINDER Specify endpoint of first axis or [Center]:").into_owned()
}
Step::EllipseCenter => t!("CYLINDER Specify center point:").into_owned(),
Step::EllipseCenterFirstAxis(_) => crate::tf!(
"CYLINDER Specify distance to first axis <{:.4}>:",
self.remembered.major_radius
).into_owned(),
Step::EllipseCenterSecondAxis(_, _) =>
t!("CYLINDER Specify endpoint of second axis:").into_owned(),
Step::EllipseSecond(_) =>
t!("CYLINDER Specify other endpoint of first axis:").into_owned(),
Step::EllipseThird(_, _) =>
t!("CYLINDER Specify endpoint of second axis:").into_owned(),
)
.into_owned(),
Step::EllipseCenterSecondAxis(_, _) => {
t!("CYLINDER Specify endpoint of second axis:").into_owned()
}
Step::EllipseSecond(_) => {
t!("CYLINDER Specify other endpoint of first axis:").into_owned()
}
Step::EllipseThird(_, _) => {
t!("CYLINDER Specify endpoint of second axis:").into_owned()
}
Step::TtrFirst => t!("CYLINDER Select first tangent object:").into_owned(),
Step::TtrSecond { .. } => t!("CYLINDER Select second tangent object:").into_owned(),
Step::TtrRadius { .. } => crate::tf!(
"CYLINDER Specify base radius <{:.4}>:",
self.remembered.major_radius
).into_owned(),
)
.into_owned(),
Step::Height => crate::tf!(
"CYLINDER Specify height or [2Point/Axis endpoint] <{:.4}>:",
self.remembered.height
).into_owned(),
Step::HeightFirstPoint =>
t!("CYLINDER Specify first point for height:").into_owned(),
Step::HeightSecondPoint(_) =>
t!("CYLINDER Specify second point for height:").into_owned(),
)
.into_owned(),
Step::HeightFirstPoint => t!("CYLINDER Specify first point for height:").into_owned(),
Step::HeightSecondPoint(_) => {
t!("CYLINDER Specify second point for height:").into_owned()
}
Step::AxisEndpoint => t!("CYLINDER Specify axis endpoint:").into_owned(),
}
}
@@ -821,9 +828,7 @@ fn push_ellipse(
const SEGMENTS: usize = 64;
for index in 0..=SEGMENTS {
let angle = index as f64 / SEGMENTS as f64 * std::f64::consts::TAU;
let point = center
+ x_axis * (x_radius * angle.cos())
+ y_axis * (y_radius * angle.sin());
let point = center + x_axis * (x_radius * angle.cos()) + y_axis * (y_radius * angle.sin());
points.push(point.as_vec3().to_array());
}
}
@@ -848,7 +853,10 @@ mod cylinder_tests {
assert!(!command.dyn_commit_as_text());
// Step 1: Center
assert!(matches!(command.on_point(DVec3::ZERO), CmdResult::NeedPoint));
assert!(matches!(
command.on_point(DVec3::ZERO),
CmdResult::NeedPoint
));
assert!(!command.dyn_commit_as_text());
// Step 2: Radius
@@ -914,4 +922,3 @@ mod cylinder_tests {
assert_eq!(defaults().lock().unwrap().height, 42.0);
}
}
+51 -60
View File
@@ -1,8 +1,8 @@
use std::sync::atomic::{AtomicU64, Ordering};
use codec::Handle;
use kernel::brep::{Body, EdgeKey, FaceKey};
use glam::DVec3;
use kernel::brep::{Body, EdgeKey, FaceKey};
use crate::command::{
CadCommand, CmdOption, CmdResult, DynAnchor, DynFieldSpec, DynGuide, DynRole, DynSpec,
@@ -186,11 +186,7 @@ impl SolidEdgeCommand {
)
}
fn preview_for_values(
&self,
value: f64,
other_value: f64,
) -> Option<(Handle, Vec<WireModel>)> {
fn preview_for_values(&self, value: f64, other_value: f64) -> Option<(Handle, Vec<WireModel>)> {
if !self.preview_active() {
return None;
}
@@ -210,12 +206,8 @@ impl SolidEdgeCommand {
)
.ok()?,
};
let mut wires = crate::scene::model::solid_model::grip_preview_wires(
&result,
handle,
[0; 2],
false,
);
let mut wires =
crate::scene::model::solid_model::grip_preview_wires(&result, handle, [0; 2], false);
for wire in &mut wires {
wire.color = self.preview_color;
wire.name = format!("{}-{}-PREVIEW", handle.value(), self.name());
@@ -226,8 +218,7 @@ impl SolidEdgeCommand {
fn rebuild_preview(&mut self) {
self.preview_wires.clear();
self.preview_hidden.clear();
if let Some((handle, wires)) =
self.preview_for_values(self.default_value, self.other_value)
if let Some((handle, wires)) = self.preview_for_values(self.default_value, self.other_value)
{
self.preview_wires = wires;
// While edges are still being picked the source stays drawn: a
@@ -367,11 +358,13 @@ impl CadCommand for SolidEdgeCommand {
(_, EdgeStep::PreviewConfirm, _) => {
crate::t!("Press Enter to accept the fillet or [Radius]:").into_owned()
}
(_, EdgeStep::Radius, _) if self.value_return == EdgeStep::PreviewConfirm => crate::tf!(
"Specify Radius or [Expression] <{:.4}>:",
self.default_value
)
.into_owned(),
(_, EdgeStep::Radius, _) if self.value_return == EdgeStep::PreviewConfirm => {
crate::tf!(
"Specify Radius or [Expression] <{:.4}>:",
self.default_value
)
.into_owned()
}
(_, EdgeStep::Radius, _) => crate::tf!(
"Enter fillet radius or [Expression] <{:.4}>:",
self.default_value
@@ -384,18 +377,15 @@ impl CadCommand for SolidEdgeCommand {
fn options(&self) -> Vec<CmdOption> {
match (self.operation, self.step, self.selection_mode) {
(EdgeOperation::Chamfer, EdgeStep::Selecting, _) => vec![
CmdOption::new("Loop", "L"),
CmdOption::new("Distance", "D"),
],
(EdgeOperation::Chamfer, EdgeStep::PickingLoop, _) => vec![
CmdOption::new("Edge", "E"),
CmdOption::new("Distance", "D"),
],
(EdgeOperation::Chamfer, EdgeStep::LoopConfirm, _) => vec![
CmdOption::new("Accept", "A"),
CmdOption::new("Next", "N"),
],
(EdgeOperation::Chamfer, EdgeStep::Selecting, _) => {
vec![CmdOption::new("Loop", "L"), CmdOption::new("Distance", "D")]
}
(EdgeOperation::Chamfer, EdgeStep::PickingLoop, _) => {
vec![CmdOption::new("Edge", "E"), CmdOption::new("Distance", "D")]
}
(EdgeOperation::Chamfer, EdgeStep::LoopConfirm, _) => {
vec![CmdOption::new("Accept", "A"), CmdOption::new("Next", "N")]
}
(EdgeOperation::Chamfer, EdgeStep::PreviewConfirm, _) => {
vec![CmdOption::new("Distance", "D")]
}
@@ -409,19 +399,17 @@ impl CadCommand for SolidEdgeCommand {
CmdOption::new("Loop", "L"),
CmdOption::new("Radius", "R"),
],
(EdgeOperation::Fillet, EdgeStep::Selecting, EdgeSelectionMode::Chain) => vec![
CmdOption::new("Edge", "E"),
CmdOption::new("Radius", "R"),
],
(EdgeOperation::Fillet, EdgeStep::Selecting, EdgeSelectionMode::Chain) => {
vec![CmdOption::new("Edge", "E"), CmdOption::new("Radius", "R")]
}
(EdgeOperation::Fillet, EdgeStep::PickingLoop, _) => vec![
CmdOption::new("Edge", "E"),
CmdOption::new("Chain", "C"),
CmdOption::new("Radius", "R"),
],
(EdgeOperation::Fillet, EdgeStep::LoopConfirm, _) => vec![
CmdOption::new("Accept", "A"),
CmdOption::new("Next", "N"),
],
(EdgeOperation::Fillet, EdgeStep::LoopConfirm, _) => {
vec![CmdOption::new("Accept", "A"), CmdOption::new("Next", "N")]
}
(EdgeOperation::Fillet, EdgeStep::PreviewConfirm, _) => {
vec![CmdOption::new("Radius", "R")]
}
@@ -452,10 +440,9 @@ impl CadCommand for SolidEdgeCommand {
if !self.pick_allowed(handle) {
return CmdResult::NeedPoint;
}
let Some(seed) = self
.body(handle)
.and_then(|body| crate::scene::model::solid_model::nearest_edge(body, point.to_array()))
else {
let Some(seed) = self.body(handle).and_then(|body| {
crate::scene::model::solid_model::nearest_edge(body, point.to_array())
}) else {
return CmdResult::NeedPoint;
};
self.handle = Some(handle);
@@ -465,8 +452,7 @@ impl CadCommand for SolidEdgeCommand {
self.loop_candidates = edge_loops(self.body(handle).expect("pick body exists"), seed);
if self.operation == EdgeOperation::Chamfer {
if let Some(base_face) = self.base_face {
self.loop_candidates
.retain(|(face, _)| *face == base_face);
self.loop_candidates.retain(|(face, _)| *face == base_face);
}
}
self.loop_index = 0;
@@ -481,15 +467,18 @@ impl CadCommand for SolidEdgeCommand {
if self.operation == EdgeOperation::Chamfer {
if self.base_face.is_none() {
self.base_face = self.body(handle).and_then(|body| {
nearest_edge_face(body, seed, point.to_array())
});
self.base_face = self
.body(handle)
.and_then(|body| nearest_edge_face(body, seed, point.to_array()));
}
let Some(base_face) = self.base_face else {
return CmdResult::NeedPoint;
};
if !edge_belongs_to_face(self.body(handle).expect("pick body exists"), seed, base_face)
{
if !edge_belongs_to_face(
self.body(handle).expect("pick body exists"),
seed,
base_face,
) {
return CmdResult::NeedPoint;
}
self.add_batch(vec![seed], point);
@@ -499,7 +488,9 @@ impl CadCommand for SolidEdgeCommand {
let edges = match self.selection_mode {
EdgeSelectionMode::Edge => vec![seed],
EdgeSelectionMode::Chain => edge_chain(self.body(handle).expect("pick body exists"), seed),
EdgeSelectionMode::Chain => {
edge_chain(self.body(handle).expect("pick body exists"), seed)
}
};
self.add_batch(edges, point);
self.rebuild_preview();
@@ -683,7 +674,11 @@ impl CadCommand for SolidEdgeCommand {
}
let (batch, _) = self.selection_batches.pop()?;
for edge in batch {
if let Some(index) = self.selected_edges.iter().position(|candidate| *candidate == edge) {
if let Some(index) = self
.selected_edges
.iter()
.position(|candidate| *candidate == edge)
{
self.selected_edges.remove(index);
}
}
@@ -691,10 +686,7 @@ impl CadCommand for SolidEdgeCommand {
self.handle = None;
self.base_face = None;
}
self.last_pick = self
.selection_batches
.last()
.map(|(_, anchor)| *anchor);
self.last_pick = self.selection_batches.last().map(|(_, anchor)| *anchor);
self.step = EdgeStep::Selecting;
self.rebuild_preview();
Some(CmdResult::NeedPoint)
@@ -739,8 +731,8 @@ impl CadCommand for SolidEdgeCommand {
self.step,
EdgeStep::Radius | EdgeStep::ChamferDistance1 | EdgeStep::ChamferDistance2
)
.then(|| self.last_pick.map(|pick| cursor.distance(pick)))
.flatten()
.then(|| self.last_pick.map(|pick| cursor.distance(pick)))
.flatten()
}
fn on_preview_wires(&mut self, cursor: DVec3) -> Vec<WireModel> {
@@ -855,8 +847,7 @@ fn edge_chain(body: &Body, seed: EdgeKey) -> Vec<EdgeKey> {
if candidate.start != vertex && candidate.end != vertex {
continue;
}
let Some(candidate_tangent) =
edge_tangent_from_vertex(body, candidate_key, vertex)
let Some(candidate_tangent) = edge_tangent_from_vertex(body, candidate_key, vertex)
else {
continue;
};
+50 -45
View File
@@ -6,9 +6,7 @@ use codec::{EntityType, Handle};
use glam::{DVec2, DVec3};
use crate::command::{CadCommand, CmdOption, CmdResult, WorkingPlane};
use crate::modules::draw::draw::polyline::{
arc_sample_points, compute_bulge, seg_exit_tangent,
};
use crate::modules::draw::draw::polyline::{arc_sample_points, compute_bulge, seg_exit_tangent};
use crate::scene::model::solid_model;
use crate::scene::model::sweep_model::{self, PolysolidJustification};
use crate::scene::model::wire_model::WireModel;
@@ -139,12 +137,9 @@ impl PolysolidCommand {
}
fn commit(&self, path: EntityType, erase_source: Option<Handle>) -> CmdResult {
let Some((solid, history)) = sweep_model::polysolid(
&path,
self.width,
self.height,
self.justification,
) else {
let Some((solid, history)) =
sweep_model::polysolid(&path, self.width, self.height, self.justification)
else {
return CmdResult::NeedPoint;
};
let Some(document) = crate::scene::convert::acis_export::solid_to_sat(&solid) else {
@@ -176,7 +171,8 @@ impl PolysolidCommand {
let first = self.vertices[0];
let bulge = compute_bulge(
self.plane.to_local(last).truncate(),
self.pending_direction.unwrap_or_else(|| self.last_tangent()),
self.pending_direction
.unwrap_or_else(|| self.last_tangent()),
self.plane.to_local(first).truncate(),
);
if let Some(value) = self.bulges.last_mut() {
@@ -197,8 +193,10 @@ impl PolysolidCommand {
self.pending_second = None;
if self.vertices.is_empty() {
self.step = Step::Start;
} else if matches!(self.step, Step::Arc | Step::ArcDirection | Step::ArcSecond | Step::ArcEnd)
{
} else if matches!(
self.step,
Step::Arc | Step::ArcDirection | Step::ArcSecond | Step::ArcEnd
) {
self.step = Step::Arc;
} else {
self.step = Step::Line;
@@ -236,8 +234,7 @@ impl PolysolidCommand {
let a = DVec2::new(a3.x, a3.y);
let m = DVec2::new(m3.x, m3.y);
let b = DVec2::new(b3.x, b3.y);
let determinant = 2.0
* (a.x * (m.y - b.y) + m.x * (b.y - a.y) + b.x * (a.y - m.y));
let determinant = 2.0 * (a.x * (m.y - b.y) + m.x * (b.y - a.y) + b.x * (a.y - m.y));
if determinant.abs() <= 1e-12 {
return 0.0;
}
@@ -245,17 +242,14 @@ impl PolysolidCommand {
let mm = m.length_squared();
let bb = b.length_squared();
let center = DVec2::new(
(aa * (m.y - b.y) + mm * (b.y - a.y) + bb * (a.y - m.y))
/ determinant,
(aa * (b.x - m.x) + mm * (a.x - b.x) + bb * (m.x - a.x))
/ determinant,
(aa * (m.y - b.y) + mm * (b.y - a.y) + bb * (a.y - m.y)) / determinant,
(aa * (b.x - m.x) + mm * (a.x - b.x) + bb * (m.x - a.x)) / determinant,
);
let start_angle = (a - center).y.atan2((a - center).x);
let middle_angle = (m - center).y.atan2((m - center).x);
let end_angle = (b - center).y.atan2((b - center).x);
let ccw = (end_angle - start_angle).rem_euclid(std::f64::consts::TAU);
let middle_ccw =
(middle_angle - start_angle).rem_euclid(std::f64::consts::TAU);
let middle_ccw = (middle_angle - start_angle).rem_euclid(std::f64::consts::TAU);
let sweep = if middle_ccw <= ccw + 1e-12 {
ccw
} else {
@@ -281,16 +275,21 @@ impl PolysolidCommand {
Step::Arc => {
let bulge = compute_bulge(
start_local.truncate(),
self.pending_direction.unwrap_or_else(|| self.last_tangent()),
self.pending_direction
.unwrap_or_else(|| self.last_tangent()),
cursor_local.truncate(),
);
arc_sample_points(start_local.as_vec3(), bulge, cursor_local.as_vec3(), 24)
.into_iter()
.map(|point| self.plane.to_world(DVec3::new(
point[0] as f64,
point[1] as f64,
point[2] as f64,
)).to_array())
.map(|point| {
self.plane
.to_world(DVec3::new(
point[0] as f64,
point[1] as f64,
point[2] as f64,
))
.to_array()
})
.collect()
}
Step::ArcEnd => {
@@ -298,11 +297,15 @@ impl PolysolidCommand {
let bulge = self.three_point_bulge(start, middle, cursor);
arc_sample_points(start_local.as_vec3(), bulge, cursor_local.as_vec3(), 24)
.into_iter()
.map(|point| self.plane.to_world(DVec3::new(
point[0] as f64,
point[1] as f64,
point[2] as f64,
)).to_array())
.map(|point| {
self.plane
.to_world(DVec3::new(
point[0] as f64,
point[1] as f64,
point[2] as f64,
))
.to_array()
})
.collect()
}
Step::ArcDirection => vec![start.to_array(), cursor.to_array()],
@@ -330,12 +333,11 @@ impl PolysolidCommand {
let segment_bulge = match self.step {
Step::Arc => compute_bulge(
self.plane.to_local(start).truncate(),
self.pending_direction.unwrap_or_else(|| self.last_tangent()),
self.pending_direction
.unwrap_or_else(|| self.last_tangent()),
self.plane.to_local(cursor).truncate(),
),
Step::ArcEnd => {
self.three_point_bulge(start, self.pending_second?, cursor)
}
Step::ArcEnd => self.three_point_bulge(start, self.pending_second?, cursor),
_ => 0.0,
};
if let Some(last) = bulges.last_mut() {
@@ -345,12 +347,8 @@ impl PolysolidCommand {
bulges.push(0.0);
let path = self.path_entity_from(&vertices, &bulges, false)?;
let (solid, _) = sweep_model::polysolid(
&path,
self.width,
self.height,
self.justification,
)?;
let (solid, _) =
sweep_model::polysolid(&path, self.width, self.height, self.justification)?;
let previews = solid_model::edge_wires(&solid)
.into_iter()
.enumerate()
@@ -393,7 +391,8 @@ impl CadCommand for PolysolidCommand {
PolysolidJustification::Right => crate::t!("Right"),
},
t!("Specify start point or [Object/Height/Width/Justify] <Object>:")
).into_owned(),
)
.into_owned(),
Step::Height => crate::tf!("Specify height <{:.4}>:", self.height).into_owned(),
Step::Width => crate::tf!("Specify width <{:.4}>:", self.width).into_owned(),
Step::Justify => crate::tf!(
@@ -403,7 +402,8 @@ impl CadCommand for PolysolidCommand {
PolysolidJustification::Center => crate::t!("Center"),
PolysolidJustification::Right => crate::t!("Right"),
}
).into_owned(),
)
.into_owned(),
Step::Object => t!("Select object:").into_owned(),
Step::Line if self.vertices.len() >= 3 => {
t!("Specify next point or [Arc/Close/Undo]:").into_owned()
@@ -470,7 +470,9 @@ impl CadCommand for PolysolidCommand {
let start = *self.vertices.last().unwrap_or(&point);
let a = self.plane.to_local(start).truncate();
let b = self.plane.to_local(point).truncate();
let tangent = self.pending_direction.unwrap_or_else(|| self.last_tangent());
let tangent = self
.pending_direction
.unwrap_or_else(|| self.last_tangent());
let bulge = compute_bulge(a, tangent, b);
self.add_segment(point, bulge)
}
@@ -519,7 +521,10 @@ impl CadCommand for PolysolidCommand {
}
fn wants_text_input(&self) -> bool {
!matches!(self.step, Step::Object | Step::ArcDirection | Step::ArcSecond | Step::ArcEnd)
!matches!(
self.step,
Step::Object | Step::ArcDirection | Step::ArcSecond | Step::ArcEnd
)
}
fn point_step_accepts_keywords(&self) -> bool {
+176 -203
View File
@@ -3,14 +3,13 @@
use codec::entities::Solid3D;
use codec::objects::SolidHistoryOperation;
use codec::EntityType;
use glam::DVec3;
use kernel::brep::Body;
use kernel::geom2d::{
fillets_between, Circle as KernelCircle, Curve as KernelCurve, Line as KernelLine,
Tolerance,
fillets_between, Circle as KernelCircle, Curve as KernelCurve, Line as KernelLine, Tolerance,
};
use glam::DVec3;
use std::sync::{Mutex, OnceLock};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Mutex, OnceLock};
use crate::command::{CadCommand, CmdOption, CmdResult, TangentObject, WorkingPlane};
use crate::scene::model::solid_model;
@@ -139,7 +138,10 @@ enum ConeStep {
EllipseSecond(DVec3),
EllipseThird(DVec3, DVec3),
TtrFirst,
TtrSecond { object: TangentObject, hit: DVec3 },
TtrSecond {
object: TangentObject,
hit: DVec3,
},
TtrRadius {
first: TangentObject,
second: TangentObject,
@@ -322,11 +324,7 @@ fn sphere_tangent_curve(object: TangentObject) -> KernelCurve {
}
}
fn sphere_ttr_centers(
first: TangentObject,
second: TangentObject,
radius: f64,
) -> Vec<DVec3> {
fn sphere_ttr_centers(first: TangentObject, second: TangentObject, radius: f64) -> Vec<DVec3> {
fillets_between(
&sphere_tangent_curve(first),
&sphere_tangent_curve(second),
@@ -347,11 +345,7 @@ fn closest_sphere_center(candidates: &[DVec3], hint: DVec3) -> Option<DVec3> {
})
}
fn sphere_through_three_points(
first: DVec3,
second: DVec3,
third: DVec3,
) -> Option<(DVec3, f64)> {
fn sphere_through_three_points(first: DVec3, second: DVec3, third: DVec3) -> Option<(DVec3, f64)> {
let circle = kernel::geom2d::arc_through_points(
[first.x, first.y],
[second.x, second.y],
@@ -540,10 +534,8 @@ impl PrimitiveCommand {
return CmdResult::NeedPoint;
};
let hint = (first_hit + second_hit) * 0.5;
let Some(center) = closest_sphere_center(
&sphere_ttr_centers(first, second, radius),
hint,
) else {
let Some(center) = closest_sphere_center(&sphere_ttr_centers(first, second, radius), hint)
else {
return CmdResult::NeedPoint;
};
self.commit_sphere(center, radius)
@@ -570,12 +562,7 @@ impl PrimitiveCommand {
CmdResult::NeedPoint
}
fn commit_torus(
&mut self,
center: DVec3,
major_radius: f64,
minor_radius: f64,
) -> CmdResult {
fn commit_torus(&mut self, center: DVec3, major_radius: f64, minor_radius: f64) -> CmdResult {
if !center.is_finite()
|| !major_radius.is_finite()
|| !minor_radius.is_finite()
@@ -608,10 +595,8 @@ impl PrimitiveCommand {
return CmdResult::NeedPoint;
};
let hint = (first_hit + second_hit) * 0.5;
let Some(center) = closest_sphere_center(
&sphere_ttr_centers(first, second, radius),
hint,
) else {
let Some(center) = closest_sphere_center(&sphere_ttr_centers(first, second, radius), hint)
else {
return CmdResult::NeedPoint;
};
self.set_torus_major_radius(center, radius)
@@ -792,12 +777,7 @@ impl PrimitiveCommand {
return None;
};
let local = crate::modules::draw::draw::circle::pick_best_ttr_candidate(
first,
second,
radius,
first_hit,
second_hit,
None,
first, second, radius, first_hit, second_hit, None,
)?;
Some(self.plane.to_world(local))
}
@@ -860,55 +840,75 @@ impl PrimitiveCommand {
fn cone_prompt(&self) -> String {
match self.cone_step {
ConeStep::BaseCenter =>
t!("CONE Specify center point of base or [3P/2P/Ttr/Elliptical]:").into_owned(),
ConeStep::BaseCenter => {
t!("CONE Specify center point of base or [3P/2P/Ttr/Elliptical]:").into_owned()
}
ConeStep::BaseRadius => crate::tf!(
"CONE Specify base radius or [Diameter] <{:.4}>:",
self.cone_defaults.base_x_radius
).into_owned(),
)
.into_owned(),
ConeStep::BaseDiameter => crate::tf!(
"CONE Specify base diameter <{:.4}>:",
self.cone_defaults.base_x_radius * 2.0
).into_owned(),
)
.into_owned(),
ConeStep::ThreePointFirst => t!("CONE Specify first point on base:").into_owned(),
ConeStep::ThreePointSecond(_) => t!("CONE Specify second point on base:").into_owned(),
ConeStep::ThreePointThird(_, _) => t!("CONE Specify third point on base:").into_owned(),
ConeStep::TwoPointFirst => t!("CONE Specify first endpoint of base diameter:").into_owned(),
ConeStep::TwoPointSecond(_) => t!("CONE Specify second endpoint of base diameter:").into_owned(),
ConeStep::EllipseFirst =>
t!("CONE Specify endpoint of first axis or [Center]:").into_owned(),
ConeStep::ThreePointThird(_, _) => {
t!("CONE Specify third point on base:").into_owned()
}
ConeStep::TwoPointFirst => {
t!("CONE Specify first endpoint of base diameter:").into_owned()
}
ConeStep::TwoPointSecond(_) => {
t!("CONE Specify second endpoint of base diameter:").into_owned()
}
ConeStep::EllipseFirst => {
t!("CONE Specify endpoint of first axis or [Center]:").into_owned()
}
ConeStep::EllipseCenter => t!("CONE Specify center point:").into_owned(),
ConeStep::EllipseCenterFirstAxis(_) => crate::tf!(
"CONE Specify distance to first axis <{:.4}>:",
self.cone_defaults.base_x_radius
)
.into_owned(),
ConeStep::EllipseCenterSecondAxis(_, _) =>
t!("CONE Specify endpoint of second axis:").into_owned(),
ConeStep::EllipseSecond(_) => t!("CONE Specify second endpoint of ellipse axis:").into_owned(),
ConeStep::EllipseThird(_, _) => t!("CONE Specify distance to other ellipse axis:").into_owned(),
ConeStep::EllipseCenterSecondAxis(_, _) => {
t!("CONE Specify endpoint of second axis:").into_owned()
}
ConeStep::EllipseSecond(_) => {
t!("CONE Specify second endpoint of ellipse axis:").into_owned()
}
ConeStep::EllipseThird(_, _) => {
t!("CONE Specify distance to other ellipse axis:").into_owned()
}
ConeStep::TtrFirst => t!("CONE Select first tangent object:").into_owned(),
ConeStep::TtrSecond { .. } => t!("CONE Select second tangent object:").into_owned(),
ConeStep::TtrRadius { .. } => crate::tf!(
"CONE Specify base radius <{:.4}>:",
self.cone_defaults.base_x_radius
).into_owned(),
)
.into_owned(),
ConeStep::Height => crate::tf!(
"CONE Specify height or [2Point/Axis endpoint/Top radius] <{:.4}>:",
self.cone_defaults.height
).into_owned(),
)
.into_owned(),
ConeStep::HeightAfterTopRadius => crate::tf!(
"CONE Specify height or [2Point/Axis endpoint] <{:.4}>:",
self.cone_defaults.height
)
.into_owned(),
ConeStep::HeightFirstPoint => t!("CONE Specify first point for height:").into_owned(),
ConeStep::HeightSecondPoint(_) => t!("CONE Specify second point for height:").into_owned(),
ConeStep::HeightSecondPoint(_) => {
t!("CONE Specify second point for height:").into_owned()
}
ConeStep::AxisEndpoint => t!("CONE Specify axis endpoint:").into_owned(),
ConeStep::TopRadius => crate::tf!(
"CONE Specify top radius <{:.4}>:",
self.cone_defaults.top_radius
).into_owned(),
)
.into_owned(),
}
}
@@ -1159,10 +1159,8 @@ impl PrimitiveCommand {
})
}
ConeStep::EllipseCenterFirstAxis(center) => (number > 0.0).then(|| {
self.cone_step = ConeStep::EllipseCenterSecondAxis(
center,
center + self.plane.x * number,
);
self.cone_step =
ConeStep::EllipseCenterSecondAxis(center, center + self.plane.x * number);
CmdResult::NeedPoint
}),
ConeStep::TtrRadius { .. } => {
@@ -1314,8 +1312,7 @@ impl PrimitiveCommand {
if !length.is_finite() || length < 1e-6 {
return false;
}
let radius = length
/ (2.0 * (std::f64::consts::PI / self.pyramid_sides as f64).sin());
let radius = length / (2.0 * (std::f64::consts::PI / self.pyramid_sides as f64).sin());
let apothem = radius * pyramid_apothem_factor(self.pyramid_sides);
let center = (first + second) * 0.5 + self.plane.z.cross(edge) * apothem;
let Some(x) = (first - center).try_normalize() else {
@@ -1462,13 +1459,21 @@ impl PrimitiveCommand {
push_segment(
&mut points,
base[index],
if self.pyramid_top_radius > 1e-9 { top[index] } else { apex },
if self.pyramid_top_radius > 1e-9 {
top[index]
} else {
apex
},
);
}
Some(wire("primitive_height_preview", points))
}
fn pyramid_base_preview(&self, frame: WorkingPlane, displayed_radius: f64) -> Option<WireModel> {
fn pyramid_base_preview(
&self,
frame: WorkingPlane,
displayed_radius: f64,
) -> Option<WireModel> {
if !displayed_radius.is_finite() || displayed_radius < 1e-9 {
return None;
}
@@ -1487,40 +1492,46 @@ impl PrimitiveCommand {
let base_default = self.pyramid_defaults.displayed_base_radius;
let top_default = self.pyramid_defaults.displayed_top_radius;
match self.pyramid_step {
PyramidStep::BaseCenter => crate::tf!(
"PYRAMID Specify center point of base or [Edge/Sides]:"
).into_owned(),
PyramidStep::Sides => crate::tf!(
"PYRAMID Enter number of sides <{}>:",
self.pyramid_sides
).into_owned(),
PyramidStep::BaseCenter => {
crate::tf!("PYRAMID Specify center point of base or [Edge/Sides]:").into_owned()
}
PyramidStep::Sides => {
crate::tf!("PYRAMID Enter number of sides <{}>:", self.pyramid_sides).into_owned()
}
PyramidStep::EdgeFirst => t!("PYRAMID Specify first endpoint of edge:").into_owned(),
PyramidStep::EdgeSecond => t!("PYRAMID Specify second endpoint of edge:").into_owned(),
PyramidStep::BaseRadius => match self.pyramid_type {
PyramidType::Circumscribed => crate::tf!(
"PYRAMID Specify base radius or [Inscribed] <{:.4}>:",
base_default
).into_owned(),
)
.into_owned(),
PyramidType::Inscribed => crate::tf!(
"PYRAMID Specify base radius or [Circumscribed] <{:.4}>:",
base_default
).into_owned(),
)
.into_owned(),
},
PyramidStep::Height => crate::tf!(
"PYRAMID Specify height or [2Point/Axis endpoint/Top radius] <{:.4}>:",
self.pyramid_defaults.height
).into_owned(),
)
.into_owned(),
PyramidStep::HeightAfterTopRadius => crate::tf!(
"PYRAMID Specify height or [2Point/Axis endpoint] <{:.4}>:",
self.pyramid_defaults.height
).into_owned(),
PyramidStep::HeightFirstPoint => t!("PYRAMID Specify first point for height:").into_owned(),
PyramidStep::HeightSecondPoint => t!("PYRAMID Specify second point for height:").into_owned(),
)
.into_owned(),
PyramidStep::HeightFirstPoint => {
t!("PYRAMID Specify first point for height:").into_owned()
}
PyramidStep::HeightSecondPoint => {
t!("PYRAMID Specify second point for height:").into_owned()
}
PyramidStep::AxisEndpoint => t!("PYRAMID Specify axis endpoint:").into_owned(),
PyramidStep::TopRadius => crate::tf!(
"PYRAMID Specify top radius <{:.4}>:",
top_default
).into_owned(),
PyramidStep::TopRadius => {
crate::tf!("PYRAMID Specify top radius <{:.4}>:", top_default).into_owned()
}
}
}
@@ -1681,11 +1692,8 @@ impl PrimitiveCommand {
.then_some(CmdResult::NeedPoint)
}
PyramidStep::TopRadius if number >= 0.0 => {
self.pyramid_top_radius = pyramid_display_to_circumradius(
number,
self.pyramid_type,
self.pyramid_sides,
);
self.pyramid_top_radius =
pyramid_display_to_circumradius(number, self.pyramid_type, self.pyramid_sides);
self.pyramid_step = PyramidStep::HeightAfterTopRadius;
Some(CmdResult::NeedPoint)
}
@@ -1811,15 +1819,9 @@ impl PrimitiveCommand {
.to_cols_array()
}
fn history_transform_axes(
&self,
origin: DVec3,
x_axis: DVec3,
y_axis: DVec3,
) -> [f64; 16] {
let to_world_vector = |value: DVec3| {
self.plane.x * value.x + self.plane.y * value.y + self.plane.z * value.z
};
fn history_transform_axes(&self, origin: DVec3, x_axis: DVec3, y_axis: DVec3) -> [f64; 16] {
let to_world_vector =
|value: DVec3| self.plane.x * value.x + self.plane.y * value.y + self.plane.z * value.z;
glam::DMat4::from_cols(
to_world_vector(x_axis).extend(0.0),
to_world_vector(y_axis).extend(0.0),
@@ -1894,7 +1896,11 @@ impl PrimitiveCommand {
Some((
placed,
solid_history::box_op(
self.history_transform_axes(box_centre(origin, x_axis, y_axis, length, width, height), x_axis, y_axis),
self.history_transform_axes(
box_centre(origin, x_axis, y_axis, length, width, height),
x_axis,
y_axis,
),
length,
width,
height,
@@ -1967,9 +1973,7 @@ impl PrimitiveCommand {
let x_axis = DVec3::new(cos, sin, 0.0);
let y_axis = DVec3::new(-sin, cos, 0.0) * self.box_width_sign;
self.box_origin = Some(
center
- x_axis * self.box_length.unwrap_or_default() * 0.5
- y_axis * width * 0.5,
center - x_axis * self.box_length.unwrap_or_default() * 0.5 - y_axis * width * 0.5,
);
}
self.box_step = BoxStep::Height;
@@ -1981,11 +1985,7 @@ impl PrimitiveCommand {
let (origin, x_axis, y_axis, length, width, height) = self.box_spec(height)?;
let mut points = Vec::new();
if self.shape == Shape::Wedge {
let near = [
origin,
origin + x_axis * length,
origin + DVec3::Z * height,
];
let near = [origin, origin + x_axis * length, origin + DVec3::Z * height];
let far = near.map(|point| point + y_axis * width);
push_loop(&mut points, &near);
push_loop(&mut points, &far);
@@ -2024,7 +2024,11 @@ impl PrimitiveCommand {
Some((
placed,
solid_history::wedge_op(
self.history_transform_axes(box_centre(origin, x_axis, y_axis, length, width, height), x_axis, y_axis),
self.history_transform_axes(
box_centre(origin, x_axis, y_axis, length, width, height),
x_axis,
y_axis,
),
length,
width,
height,
@@ -2075,11 +2079,7 @@ impl PrimitiveCommand {
let center = [c.x, c.y, c.z];
(
solid_model::torus_solid(center, major, minor),
solid_history::torus_op(
self.history_transform(c),
major,
minor,
),
solid_history::torus_op(self.history_transform(c), major, minor),
)
}
};
@@ -2140,8 +2140,11 @@ impl CadCommand for PrimitiveCommand {
/// the mouse instead of the ray's hit on the working plane below.
fn cursor_plane(&self) -> Option<(DVec3, DVec3)> {
let origin = if self.shape == Shape::Cone {
matches!(self.cone_step, ConeStep::BaseRadius | ConeStep::BaseDiameter)
.then_some(self.cone_frame?.origin)
matches!(
self.cone_step,
ConeStep::BaseRadius | ConeStep::BaseDiameter
)
.then_some(self.cone_frame?.origin)
} else if self.shape == Shape::Pyramid {
(self.pyramid_step == PyramidStep::BaseRadius).then_some(self.pyramid_frame?.origin)
} else if self.shape.rectangular() {
@@ -2156,8 +2159,11 @@ impl CadCommand for PrimitiveCommand {
fn cursor_axis(&self) -> Option<(DVec3, DVec3)> {
if self.shape == Shape::Cone {
return matches!(self.cone_step, ConeStep::Height | ConeStep::HeightAfterTopRadius)
.then(|| {
return matches!(
self.cone_step,
ConeStep::Height | ConeStep::HeightAfterTopRadius
)
.then(|| {
let frame = self.cone_frame.unwrap_or(self.plane);
(frame.origin, frame.z.normalize_or_zero())
});
@@ -2324,9 +2330,7 @@ impl CadCommand for PrimitiveCommand {
SphereStep::ThreePointThird(_, _) => {
t!("SPHERE Specify third point on sphere:").into_owned()
}
SphereStep::TtrFirst => {
t!("SPHERE Select first tangent object:").into_owned()
}
SphereStep::TtrFirst => t!("SPHERE Select first tangent object:").into_owned(),
SphereStep::TtrSecond { .. } => {
t!("SPHERE Select second tangent object:").into_owned()
}
@@ -2339,9 +2343,7 @@ impl CadCommand for PrimitiveCommand {
}
if self.shape == Shape::Torus {
return match self.torus_step {
TorusStep::Center => {
t!("TORUS Specify center point or [3P/2P/Ttr]:").into_owned()
}
TorusStep::Center => t!("TORUS Specify center point or [3P/2P/Ttr]:").into_owned(),
TorusStep::Radius(_) => crate::tf!(
"TORUS Specify radius or [Diameter] <{:.4}>:",
self.torus_defaults.major_radius
@@ -2367,9 +2369,7 @@ impl CadCommand for PrimitiveCommand {
TorusStep::ThreePointThird(_, _) => {
t!("TORUS Specify third point on torus:").into_owned()
}
TorusStep::TtrFirst => {
t!("TORUS Select first tangent object:").into_owned()
}
TorusStep::TtrFirst => t!("TORUS Select first tangent object:").into_owned(),
TorusStep::TtrSecond { .. } => {
t!("TORUS Select second tangent object:").into_owned()
}
@@ -2400,12 +2400,8 @@ impl CadCommand for PrimitiveCommand {
return t!("%{n} Specify height <Enter for default>:", n = n).into_owned();
}
match (self.shape, self.pts.len()) {
(shape, 0) if shape.radial() => {
t!("%{n} Specify center point:", n = n).into_owned()
}
(shape, _) if shape.radial() => {
t!("%{n} Specify radius:", n = n).into_owned()
}
(shape, 0) if shape.radial() => t!("%{n} Specify center point:", n = n).into_owned(),
(shape, _) if shape.radial() => t!("%{n} Specify radius:", n = n).into_owned(),
(_, 0) => t!("%{n} Specify first corner:", n = n).into_owned(),
(_, _) => t!("%{n} Specify opposite corner:", n = n).into_owned(),
}
@@ -2491,8 +2487,7 @@ impl CadCommand for PrimitiveCommand {
CmdResult::NeedPoint
}
SphereStep::ThreePointThird(first, second) => {
let Some((center, radius)) =
sphere_through_three_points(first, second, local)
let Some((center, radius)) = sphere_through_three_points(first, second, local)
else {
return CmdResult::NeedPoint;
};
@@ -2524,10 +2519,9 @@ impl CadCommand for PrimitiveCommand {
self.torus_step = TorusStep::TwoPointSecond(local);
CmdResult::NeedPoint
}
TorusStep::TwoPointSecond(first) => self.set_torus_major_radius(
(first + local) * 0.5,
first.distance(local) * 0.5,
),
TorusStep::TwoPointSecond(first) => {
self.set_torus_major_radius((first + local) * 0.5, first.distance(local) * 0.5)
}
TorusStep::ThreePointFirst => {
self.torus_step = TorusStep::ThreePointSecond(local);
CmdResult::NeedPoint
@@ -2537,8 +2531,7 @@ impl CadCommand for PrimitiveCommand {
CmdResult::NeedPoint
}
TorusStep::ThreePointThird(first, second) => {
let Some((center, radius)) =
sphere_through_three_points(first, second, local)
let Some((center, radius)) = sphere_through_three_points(first, second, local)
else {
return CmdResult::NeedPoint;
};
@@ -2609,7 +2602,7 @@ impl CadCommand for PrimitiveCommand {
(delta.length(), 0.0)
};
self.set_box_cube(size, angle)
.unwrap_or(CmdResult::NeedPoint)
.unwrap_or(CmdResult::NeedPoint)
}
BoxStep::Length => {
let delta = local - self.pts[0];
@@ -2678,9 +2671,7 @@ impl CadCommand for PrimitiveCommand {
SphereStep::Diameter(center) => {
self.commit_sphere(center, self.sphere_default_radius)
}
SphereStep::TtrRadius { .. } => {
self.sphere_ttr_result(self.sphere_default_radius)
}
SphereStep::TtrRadius { .. } => self.sphere_ttr_result(self.sphere_default_radius),
_ => CmdResult::Cancel,
};
}
@@ -2863,16 +2854,13 @@ impl CadCommand for PrimitiveCommand {
if self.shape == Shape::Cone {
return matches!(
self.cone_step,
ConeStep::BaseCenter
| ConeStep::BaseRadius
| ConeStep::EllipseFirst
ConeStep::BaseCenter | ConeStep::BaseRadius | ConeStep::EllipseFirst
);
}
if self.shape == Shape::Pyramid {
return matches!(
self.pyramid_step,
PyramidStep::BaseCenter
| PyramidStep::BaseRadius
PyramidStep::BaseCenter | PyramidStep::BaseRadius
);
}
self.shape.rectangular()
@@ -3079,13 +3067,12 @@ impl CadCommand for PrimitiveCommand {
center.distance(local) * 0.5,
self.torus_defaults.minor_radius,
),
TorusStep::TwoPointSecond(first) | TorusStep::ThreePointSecond(first) => {
self.torus_preview(
TorusStep::TwoPointSecond(first) | TorusStep::ThreePointSecond(first) => self
.torus_preview(
(first + local) * 0.5,
first.distance(local) * 0.5,
self.torus_defaults.minor_radius,
)
}
),
TorusStep::ThreePointThird(first, second) => {
let (center, radius) = sphere_through_three_points(first, second, local)?;
self.torus_preview(center, radius, self.torus_defaults.minor_radius)
@@ -3098,10 +3085,8 @@ impl CadCommand for PrimitiveCommand {
} => {
let radius = second_hit.distance(local);
let hint = (first_hit + second_hit) * 0.5;
let center = closest_sphere_center(
&sphere_ttr_centers(first, second, radius),
hint,
)?;
let center =
closest_sphere_center(&sphere_ttr_centers(first, second, radius), hint)?;
self.torus_preview(center, radius, self.torus_defaults.minor_radius)
}
TorusStep::TubeRadius {
@@ -3148,10 +3133,8 @@ impl CadCommand for PrimitiveCommand {
} => {
let radius = second_hit.distance(local);
let hint = (first_hit + second_hit) * 0.5;
let center = closest_sphere_center(
&sphere_ttr_centers(first, second, radius),
hint,
)?;
let center =
closest_sphere_center(&sphere_ttr_centers(first, second, radius), hint)?;
self.sphere_preview(center, radius)
}
_ => None,
@@ -3177,16 +3160,8 @@ impl CadCommand for PrimitiveCommand {
let points = if self.box_centered {
let delta = local - first;
vec![
DVec3::new(
first.x - delta.x.abs(),
first.y - delta.y.abs(),
first.z,
),
DVec3::new(
first.x + delta.x.abs(),
first.y + delta.y.abs(),
first.z,
),
DVec3::new(first.x - delta.x.abs(), first.y - delta.y.abs(), first.z),
DVec3::new(first.x + delta.x.abs(), first.y + delta.y.abs(), first.z),
]
} else {
vec![first, local]
@@ -3221,9 +3196,8 @@ impl CadCommand for PrimitiveCommand {
let (sin, cos) = preview.box_angle.sin_cos();
let x_axis = DVec3::new(cos, sin, 0.0);
let y_axis = DVec3::new(-sin, cos, 0.0);
preview.box_origin = Some(
self.pts[0] - x_axis * size * 0.5 - y_axis * size * 0.5,
);
preview.box_origin =
Some(self.pts[0] - x_axis * size * 0.5 - y_axis * size * 0.5);
}
preview.rectangular_preview(size)
}
@@ -3274,9 +3248,7 @@ impl CadCommand for PrimitiveCommand {
let (anchor, role) = match self.torus_step {
TorusStep::Radius(center) => (center, DynRole::Radius),
TorusStep::Diameter(center) => (center, DynRole::Diameter),
TorusStep::TtrRadius { second_hit, .. } => {
(second_hit, DynRole::Radius)
}
TorusStep::TtrRadius { second_hit, .. } => (second_hit, DynRole::Radius),
TorusStep::TubeRadius { center, .. } => (center, DynRole::Radius),
TorusStep::TubeDiameter { center, .. } => (center, DynRole::Diameter),
_ => return None,
@@ -3293,9 +3265,7 @@ impl CadCommand for PrimitiveCommand {
let (anchor, role) = match self.sphere_step {
SphereStep::Radius(center) => (center, DynRole::Radius),
SphereStep::Diameter(center) => (center, DynRole::Diameter),
SphereStep::TtrRadius { second_hit, .. } => {
(second_hit, DynRole::Radius)
}
SphereStep::TtrRadius { second_hit, .. } => (second_hit, DynRole::Radius),
_ => return None,
};
return Some(DynSpec {
@@ -3309,9 +3279,7 @@ impl CadCommand for PrimitiveCommand {
if self.shape == Shape::Cone {
let frame = self.cone_frame.unwrap_or(self.plane);
let (anchor, role) = match self.cone_step {
ConeStep::BaseRadius | ConeStep::TopRadius => {
(frame.origin, DynRole::Radius)
}
ConeStep::BaseRadius | ConeStep::TopRadius => (frame.origin, DynRole::Radius),
ConeStep::BaseDiameter => (frame.origin, DynRole::Diameter),
ConeStep::TtrRadius { second_hit, .. } => {
(self.plane.to_world(second_hit), DynRole::Radius)
@@ -3338,15 +3306,11 @@ impl CadCommand for PrimitiveCommand {
if self.shape == Shape::Pyramid {
let frame = self.pyramid_frame.unwrap_or(self.plane);
let (anchor, role) = match self.pyramid_step {
PyramidStep::BaseRadius | PyramidStep::TopRadius => {
(frame.origin, DynRole::Radius)
}
PyramidStep::BaseRadius | PyramidStep::TopRadius => (frame.origin, DynRole::Radius),
PyramidStep::Height
| PyramidStep::HeightAfterTopRadius
| PyramidStep::AxisEndpoint => (frame.origin, DynRole::Height),
PyramidStep::HeightSecondPoint => {
(self.pyramid_height_first?, DynRole::Height)
}
PyramidStep::HeightSecondPoint => (self.pyramid_height_first?, DynRole::Height),
_ => return None,
};
return Some(DynSpec {
@@ -3391,9 +3355,7 @@ impl CadCommand for PrimitiveCommand {
TorusStep::Diameter(center) | TorusStep::TubeDiameter { center, .. } => {
Some(center.distance(local))
}
TorusStep::TtrRadius { second_hit, .. } => {
Some(second_hit.distance(local))
}
TorusStep::TtrRadius { second_hit, .. } => Some(second_hit.distance(local)),
_ => None,
};
}
@@ -3402,9 +3364,7 @@ impl CadCommand for PrimitiveCommand {
return match self.sphere_step {
SphereStep::Radius(center) => Some(center.distance(local)),
SphereStep::Diameter(center) => Some(center.distance(local)),
SphereStep::TtrRadius { second_hit, .. } => {
Some(second_hit.distance(local))
}
SphereStep::TtrRadius { second_hit, .. } => Some(second_hit.distance(local)),
_ => None,
};
}
@@ -3429,7 +3389,9 @@ impl CadCommand for PrimitiveCommand {
self.cone_height_at(cursor).map(f64::abs)
}
ConeStep::HeightSecondPoint(first) => Some(first.distance(cursor)),
ConeStep::AxisEndpoint => self.cone_frame.map(|frame| frame.origin.distance(cursor)),
ConeStep::AxisEndpoint => {
self.cone_frame.map(|frame| frame.origin.distance(cursor))
}
_ => None,
};
}
@@ -3443,12 +3405,10 @@ impl CadCommand for PrimitiveCommand {
PyramidStep::Height | PyramidStep::HeightAfterTopRadius => {
self.pyramid_height_at(cursor).map(f64::abs)
}
PyramidStep::HeightSecondPoint => {
Some(self.pyramid_height_first?.distance(cursor))
}
PyramidStep::AxisEndpoint => {
self.pyramid_frame.map(|frame| frame.origin.distance(cursor))
}
PyramidStep::HeightSecondPoint => Some(self.pyramid_height_first?.distance(cursor)),
PyramidStep::AxisEndpoint => self
.pyramid_frame
.map(|frame| frame.origin.distance(cursor)),
_ => None,
};
}
@@ -3633,7 +3593,11 @@ fn push_break(points: &mut Vec<[f32; 3]>) {
fn push_loop<const N: usize>(points: &mut Vec<[f32; 3]>, path: &[DVec3; N]) {
push_break(points);
points.extend(path.iter().chain(path.first()).map(|point| point.as_vec3().to_array()));
points.extend(
path.iter()
.chain(path.first())
.map(|point| point.as_vec3().to_array()),
);
}
fn push_path_loop(points: &mut Vec<[f32; 3]>, path: &[DVec3]) {
@@ -3641,7 +3605,11 @@ fn push_path_loop(points: &mut Vec<[f32; 3]>, path: &[DVec3]) {
return;
}
push_break(points);
points.extend(path.iter().chain(path.first()).map(|point| point.as_vec3().to_array()));
points.extend(
path.iter()
.chain(path.first())
.map(|point| point.as_vec3().to_array()),
);
}
fn push_segment(points: &mut Vec<[f32; 3]>, a: DVec3, b: DVec3) {
@@ -3666,9 +3634,7 @@ fn push_ellipse_world(
const SEGMENTS: usize = 64;
for index in 0..=SEGMENTS {
let angle = index as f64 / SEGMENTS as f64 * std::f64::consts::TAU;
let point = center
+ x_axis * (x_radius * angle.cos())
+ y_axis * (y_radius * angle.sin());
let point = center + x_axis * (x_radius * angle.cos()) + y_axis * (y_radius * angle.sin());
points.push(point.as_vec3().to_array());
}
}
@@ -3725,13 +3691,20 @@ fn wire(name: &str, points: Vec<[f32; 3]>) -> WireModel {
plinegen: true,
fill_tris: vec![],
fill_tris_low: Vec::new(),
..Default::default()
}
}
}
/// Box and wedge histories are framed at the centre of their bounding box,
/// as the reference stores them (lengths stay positive).
fn box_centre(origin: DVec3, x_axis: DVec3, y_axis: DVec3, length: f64, width: f64, height: f64) -> DVec3 {
fn box_centre(
origin: DVec3,
x_axis: DVec3,
y_axis: DVec3,
length: f64,
width: f64,
height: f64,
) -> DVec3 {
origin + x_axis * (length * 0.5) + y_axis * (width * 0.5) + DVec3::Z * (height * 0.5)
}
+69 -37
View File
@@ -100,7 +100,12 @@ impl SectionPlaneCommand {
]
}
fn frame_extents(&self, centre: DVec3, tangent: DVec3, vertical: DVec3) -> (f64, f64, f64, f64) {
fn frame_extents(
&self,
centre: DVec3,
tangent: DVec3,
vertical: DVec3,
) -> (f64, f64, f64, f64) {
let mut t_min = f64::INFINITY;
let mut t_max = f64::NEG_INFINITY;
let mut v_min = f64::INFINITY;
@@ -122,7 +127,12 @@ impl SectionPlaneCommand {
} else {
1.0
};
(t_min - t_margin, t_max + t_margin, v_min - v_margin, v_max + v_margin)
(
t_min - t_margin,
t_max + t_margin,
v_min - v_margin,
v_max + v_margin,
)
}
fn entity(&self, vertices: Vec<DVec3>, vertical: DVec3, positive_view: bool) -> EntityType {
@@ -140,8 +150,12 @@ impl SectionPlaneCommand {
SectionKind::Slice => (span / 60.0).max(1e-4),
SectionKind::Boundary | SectionKind::Volume => span,
};
let back_line_vertices = if matches!(self.kind, SectionKind::Boundary | SectionKind::Volume) {
vertices.iter().map(|point| *point + viewing * depth).collect()
let back_line_vertices = if matches!(self.kind, SectionKind::Boundary | SectionKind::Volume)
{
vertices
.iter()
.map(|point| *point + viewing * depth)
.collect()
} else {
Vec::new()
};
@@ -185,7 +199,10 @@ impl SectionPlaneCommand {
};
let tangent = viewing.cross(vertical).normalize_or(DVec3::X);
let (t_min, t_max, _, _) = self.frame_extents(point, tangent, vertical);
(vec![point + tangent * t_min, point + tangent * t_max], vertical)
(
vec![point + tangent * t_min, point + tangent * t_max],
vertical,
)
}
fn orthographic(&self, kind: Orthographic) -> EntityType {
@@ -324,7 +341,11 @@ impl CadCommand for SectionPlaneCommand {
CmdResult::NeedPoint
}
Step::DrawNext => {
if self.draw_points.last().is_some_and(|last| last.distance(point) <= 1e-9) {
if self
.draw_points
.last()
.is_some_and(|last| last.distance(point) <= 1e-9)
{
self.notice = Some("SECTIONPLANE Consecutive section points must differ:");
} else {
self.draw_points.push(point);
@@ -352,24 +373,26 @@ impl CadCommand for SectionPlaneCommand {
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
let keyword = text.trim().to_ascii_uppercase();
Some(match self.step {
Step::Locate => match keyword.as_str() {
"D" | "DRAW" | "DRAWSECTION" => {
self.step = Step::DrawStart;
CmdResult::NeedPoint
Step::Locate => {
match keyword.as_str() {
"D" | "DRAW" | "DRAWSECTION" => {
self.step = Step::DrawStart;
CmdResult::NeedPoint
}
"O" | "ORTHO" | "ORTHOGRAPHIC" => {
self.step = Step::Orthographic;
CmdResult::NeedPoint
}
"T" | "TYPE" => {
self.step = Step::Type;
CmdResult::NeedPoint
}
_ => {
self.notice = Some("SECTIONPLANE Unknown option. Choose Draw section, Orthographic, or Type:");
CmdResult::NeedPoint
}
}
"O" | "ORTHO" | "ORTHOGRAPHIC" => {
self.step = Step::Orthographic;
CmdResult::NeedPoint
}
"T" | "TYPE" => {
self.step = Step::Type;
CmdResult::NeedPoint
}
_ => {
self.notice = Some("SECTIONPLANE Unknown option. Choose Draw section, Orthographic, or Type:");
CmdResult::NeedPoint
}
},
}
Step::Orthographic => {
let kind = match keyword.as_str() {
"F" | "FRONT" => Some(Orthographic::Front),
@@ -394,7 +417,9 @@ impl CadCommand for SectionPlaneCommand {
"B" | "BOUNDARY" => self.choose_kind(SectionKind::Boundary),
"V" | "VOLUME" => self.choose_kind(SectionKind::Volume),
_ => {
self.notice = Some("SECTIONPLANE Unknown type. Choose Plane, Slice, Boundary, or Volume:");
self.notice = Some(
"SECTIONPLANE Unknown type. Choose Plane, Slice, Boundary, or Volume:",
);
CmdResult::NeedPoint
}
},
@@ -432,7 +457,11 @@ impl CadCommand for SectionPlaneCommand {
self.step = Step::Through;
return CmdResult::NeedPoint;
}
let Some(face_normal) = self.picked_direction.take().filter(|normal| normal.length_squared() > 1e-18) else {
let Some(face_normal) = self
.picked_direction
.take()
.filter(|normal| normal.length_squared() > 1e-18)
else {
self.notice = Some("SECTIONPLANE Select a planar face or specify a point:");
return CmdResult::NeedPoint;
};
@@ -451,12 +480,20 @@ impl CadCommand for SectionPlaneCommand {
return None;
}
self.draw_points.pop();
self.step = if self.draw_points.is_empty() { Step::DrawStart } else { Step::DrawNext };
self.step = if self.draw_points.is_empty() {
Step::DrawStart
} else {
Step::DrawNext
};
Some(CmdResult::NeedPoint)
}
fn on_preview_wires(&mut self, point: DVec3) -> Vec<WireModel> {
let mut points = self.draw_points.iter().map(|p| p.to_array()).collect::<Vec<_>>();
let mut points = self
.draw_points
.iter()
.map(|p| p.to_array())
.collect::<Vec<_>>();
match self.step {
Step::Through => {
if let Some(first) = self.first {
@@ -505,10 +542,7 @@ mod tests {
#[test]
fn slice_uses_persistent_thickness_without_boundary_geometry() {
let mut command = SectionPlaneCommand::new(
Some((DVec3::ZERO, DVec3::splat(10.0))),
3,
);
let mut command = SectionPlaneCommand::new(Some((DVec3::ZERO, DVec3::splat(10.0))), 3);
command.kind = SectionKind::Slice;
let entity = command.entity(vec![DVec3::ZERO, DVec3::Y * 6.0], DVec3::Z, true);
let (entity, section) = data(&entity);
@@ -516,16 +550,14 @@ mod tests {
assert_eq!(section.state, 1);
assert!(section.back_line_vertices.is_empty());
assert!(crate::entities::extended::section_is_slice(entity));
assert!((crate::entities::extended::section_slice_depth(entity).unwrap() - 0.1).abs()
< 1e-12);
assert!(
(crate::entities::extended::section_slice_depth(entity).unwrap() - 0.1).abs() < 1e-12
);
}
#[test]
fn boundary_keeps_a_real_back_line_and_no_slice_marker() {
let mut command = SectionPlaneCommand::new(
Some((DVec3::ZERO, DVec3::splat(10.0))),
1,
);
let mut command = SectionPlaneCommand::new(Some((DVec3::ZERO, DVec3::splat(10.0))), 1);
command.kind = SectionKind::Boundary;
let entity = command.entity(vec![DVec3::ZERO, DVec3::Y * 6.0], DVec3::Z, true);
let (entity, section) = data(&entity);
+13 -15
View File
@@ -1,6 +1,6 @@
use codec::{EntityType, Handle};
use kernel::space::Plane;
use glam::DVec3;
use kernel::space::Plane;
use crate::command::{CadCommand, CmdOption, CmdResult, SelectionEntity, WorkingPlane};
use crate::scene::model::wire_model::WireModel;
@@ -49,7 +49,11 @@ impl SliceCommand {
preview_center: DVec3,
preview_radius: f64,
) -> Self {
let step = if targets.is_empty() { Step::Targets } else { Step::FirstPoint };
let step = if targets.is_empty() {
Step::Targets
} else {
Step::FirstPoint
};
Self {
step,
targets,
@@ -98,8 +102,7 @@ impl SliceCommand {
) {
return None;
}
crate::scene::model::presspull_model::profile_geometry(entity)
.map(|(plane, _, _)| plane)
crate::scene::model::presspull_model::profile_geometry(entity).map(|(plane, _, _)| plane)
}
fn finish_surface(&mut self, keep_point: Option<DVec3>) -> CmdResult {
@@ -314,7 +317,10 @@ impl CadCommand for SliceCommand {
}
fn point_step_accepts_keywords(&self) -> bool {
matches!(self.step, Step::FirstPoint | Step::Side(_) | Step::SurfaceSide)
matches!(
self.step,
Step::FirstPoint | Step::Side(_) | Step::SurfaceSide
)
}
fn is_selection_gathering(&self) -> bool {
@@ -438,11 +444,7 @@ impl CadCommand for SliceCommand {
_ => None,
};
if let Some(plane) = plane {
wires.extend(plane_grid(
plane,
self.preview_center,
self.preview_radius,
));
wires.extend(plane_grid(plane, self.preview_center, self.preview_radius));
}
wires
}
@@ -470,11 +472,7 @@ fn planar_body_plane(body: &kernel::brep::Body) -> Option<Plane> {
fn plane_grid(plane: Plane, centre: DVec3, radius: f64) -> Vec<WireModel> {
let normal = DVec3::from_array(plane.normal().unwrap_or([0.0, 0.0, 1.0]));
let projected = centre
- normal
* plane
.distance_to(centre.to_array())
.unwrap_or_default();
let projected = centre - normal * plane.distance_to(centre.to_array()).unwrap_or_default();
let x = DVec3::from_array(plane.x_axis);
let y = DVec3::from_array(plane.y_axis);
let color = [0.2, 0.55, 1.0, 1.0];