forked from 1b-agent/opencadstudio-upstream
style: apply cargo fmt across workspace
This commit is contained in:
1 parent
85710e5454
commit
9cf9ff2c5c
531 files changed
+57740
-33906
No files matched your search
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
|
||||
Reference in new issue
Block a user