forked from 1b-agent/opencadstudio-upstream
Merge pull request #955 from ramox81/codex/torus-command-properties
Align torus construction and properties
This commit is contained in:
4 files changed
+720
-95
No files matched your search
Generated
+1
-1
@@ -878,7 +878,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "cadkernel"
|
name = "cadkernel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=67ff1f59f380afcfd939fa98883012a0c7b109f7#67ff1f59f380afcfd939fa98883012a0c7b109f7"
|
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=b0d8dfe6ae6d0bf239e4cc82ee644ba968f16ca4#b0d8dfe6ae6d0bf239e4cc82ee644ba968f16ca4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"acadrust",
|
"acadrust",
|
||||||
"cavalier_contours",
|
"cavalier_contours",
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ rfd = "0.17"
|
|||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "a0f7d444f1607bc4b2c881060cbe7ea1014253cb", features = ["serde"] }
|
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "a0f7d444f1607bc4b2c881060cbe7ea1014253cb", features = ["serde"] }
|
||||||
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "67ff1f59f380afcfd939fa98883012a0c7b109f7", features = ["acis", "offset"] }
|
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "b0d8dfe6ae6d0bf239e4cc82ee644ba968f16ca4", features = ["acis", "offset"] }
|
||||||
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
|
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
|
||||||
flate2 = "1"
|
flate2 = "1"
|
||||||
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
|
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
|
||||||
|
|||||||
@@ -67,6 +67,61 @@ enum SphereStep {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum TorusStep {
|
||||||
|
Center,
|
||||||
|
Radius(DVec3),
|
||||||
|
Diameter(DVec3),
|
||||||
|
TwoPointFirst,
|
||||||
|
TwoPointSecond(DVec3),
|
||||||
|
ThreePointFirst,
|
||||||
|
ThreePointSecond(DVec3),
|
||||||
|
ThreePointThird(DVec3, DVec3),
|
||||||
|
TtrFirst,
|
||||||
|
TtrSecond {
|
||||||
|
object: TangentObject,
|
||||||
|
hit: DVec3,
|
||||||
|
},
|
||||||
|
TtrRadius {
|
||||||
|
first: TangentObject,
|
||||||
|
second: TangentObject,
|
||||||
|
first_hit: DVec3,
|
||||||
|
second_hit: DVec3,
|
||||||
|
},
|
||||||
|
TubeRadius {
|
||||||
|
center: DVec3,
|
||||||
|
major_radius: f64,
|
||||||
|
},
|
||||||
|
TubeDiameter {
|
||||||
|
center: DVec3,
|
||||||
|
major_radius: f64,
|
||||||
|
},
|
||||||
|
TubeTwoPointFirst {
|
||||||
|
center: DVec3,
|
||||||
|
major_radius: f64,
|
||||||
|
},
|
||||||
|
TubeTwoPointSecond {
|
||||||
|
center: DVec3,
|
||||||
|
major_radius: f64,
|
||||||
|
first: DVec3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct TorusDefaults {
|
||||||
|
major_radius: f64,
|
||||||
|
minor_radius: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TorusDefaults {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
major_radius: 100.0,
|
||||||
|
minor_radius: 25.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum ConeStep {
|
enum ConeStep {
|
||||||
BaseCenter,
|
BaseCenter,
|
||||||
@@ -180,6 +235,33 @@ fn remember_sphere_radius(radius: f64) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn torus_defaults_store() -> &'static Mutex<TorusDefaults> {
|
||||||
|
static VALUE: OnceLock<Mutex<TorusDefaults>> = OnceLock::new();
|
||||||
|
VALUE.get_or_init(|| Mutex::new(TorusDefaults::default()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn torus_defaults() -> TorusDefaults {
|
||||||
|
torus_defaults_store()
|
||||||
|
.lock()
|
||||||
|
.map(|value| *value)
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remember_torus_defaults(major_radius: f64, minor_radius: f64) {
|
||||||
|
if major_radius.is_finite()
|
||||||
|
&& minor_radius.is_finite()
|
||||||
|
&& major_radius > 1e-6
|
||||||
|
&& minor_radius > 1e-6
|
||||||
|
{
|
||||||
|
if let Ok(mut value) = torus_defaults_store().lock() {
|
||||||
|
*value = TorusDefaults {
|
||||||
|
major_radius,
|
||||||
|
minor_radius,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn sphere_tangent_local(object: TangentObject, plane: WorkingPlane) -> TangentObject {
|
fn sphere_tangent_local(object: TangentObject, plane: WorkingPlane) -> TangentObject {
|
||||||
match object {
|
match object {
|
||||||
TangentObject::Line { p1, p2 } => TangentObject::Line {
|
TangentObject::Line { p1, p2 } => TangentObject::Line {
|
||||||
@@ -330,6 +412,8 @@ pub struct PrimitiveCommand {
|
|||||||
box_height_anchor: Option<DVec3>,
|
box_height_anchor: Option<DVec3>,
|
||||||
sphere_step: SphereStep,
|
sphere_step: SphereStep,
|
||||||
sphere_default_radius: f64,
|
sphere_default_radius: f64,
|
||||||
|
torus_step: TorusStep,
|
||||||
|
torus_defaults: TorusDefaults,
|
||||||
cone_step: ConeStep,
|
cone_step: ConeStep,
|
||||||
cone_frame: Option<WorkingPlane>,
|
cone_frame: Option<WorkingPlane>,
|
||||||
cone_base_x_radius: f64,
|
cone_base_x_radius: f64,
|
||||||
@@ -372,6 +456,8 @@ impl PrimitiveCommand {
|
|||||||
box_height_anchor: None,
|
box_height_anchor: None,
|
||||||
sphere_step: SphereStep::Center,
|
sphere_step: SphereStep::Center,
|
||||||
sphere_default_radius: sphere_radius_default(),
|
sphere_default_radius: sphere_radius_default(),
|
||||||
|
torus_step: TorusStep::Center,
|
||||||
|
torus_defaults: torus_defaults(),
|
||||||
cone_step: ConeStep::BaseCenter,
|
cone_step: ConeStep::BaseCenter,
|
||||||
cone_frame: None,
|
cone_frame: None,
|
||||||
cone_base_x_radius: remembered.base_x_radius,
|
cone_base_x_radius: remembered.base_x_radius,
|
||||||
@@ -439,6 +525,81 @@ impl PrimitiveCommand {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_torus_major_radius(&mut self, center: DVec3, major_radius: f64) -> CmdResult {
|
||||||
|
if !center.is_finite() || !major_radius.is_finite() || major_radius <= 1e-6 {
|
||||||
|
return CmdResult::NeedPoint;
|
||||||
|
}
|
||||||
|
self.torus_step = TorusStep::TubeRadius {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
};
|
||||||
|
CmdResult::NeedPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
|| major_radius <= 1e-6
|
||||||
|
|| minor_radius <= 1e-6
|
||||||
|
{
|
||||||
|
return CmdResult::NeedPoint;
|
||||||
|
}
|
||||||
|
self.pts = vec![
|
||||||
|
center,
|
||||||
|
center + DVec3::X * major_radius,
|
||||||
|
center + DVec3::X * minor_radius,
|
||||||
|
];
|
||||||
|
self.torus_defaults = TorusDefaults {
|
||||||
|
major_radius,
|
||||||
|
minor_radius,
|
||||||
|
};
|
||||||
|
remember_torus_defaults(major_radius, minor_radius);
|
||||||
|
self.commit(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn torus_ttr_result(&mut self, radius: f64) -> CmdResult {
|
||||||
|
let TorusStep::TtrRadius {
|
||||||
|
first,
|
||||||
|
second,
|
||||||
|
first_hit,
|
||||||
|
second_hit,
|
||||||
|
} = self.torus_step
|
||||||
|
else {
|
||||||
|
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 {
|
||||||
|
return CmdResult::NeedPoint;
|
||||||
|
};
|
||||||
|
self.set_torus_major_radius(center, radius)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn torus_preview(
|
||||||
|
&self,
|
||||||
|
center: DVec3,
|
||||||
|
major_radius: f64,
|
||||||
|
minor_radius: f64,
|
||||||
|
) -> Option<WireModel> {
|
||||||
|
if !center.is_finite()
|
||||||
|
|| !major_radius.is_finite()
|
||||||
|
|| !minor_radius.is_finite()
|
||||||
|
|| major_radius <= 1e-6
|
||||||
|
|| minor_radius <= 1e-6
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(self.place_preview(torus_wire(center, major_radius, minor_radius)))
|
||||||
|
}
|
||||||
|
|
||||||
fn cone_frame_at(&self, center: DVec3) -> WorkingPlane {
|
fn cone_frame_at(&self, center: DVec3) -> WorkingPlane {
|
||||||
WorkingPlane {
|
WorkingPlane {
|
||||||
origin: center,
|
origin: center,
|
||||||
@@ -1867,15 +2028,11 @@ impl PrimitiveCommand {
|
|||||||
Shape::Pyramid => return None,
|
Shape::Pyramid => return None,
|
||||||
Shape::Torus => {
|
Shape::Torus => {
|
||||||
let c = self.pts[0];
|
let c = self.pts[0];
|
||||||
let first = (self.pts[1] - c).length();
|
let major = (self.pts[1] - c).length();
|
||||||
let second = (self.pts[2] - c).length();
|
let minor = (self.pts[2] - c).length();
|
||||||
let outer = first.max(second);
|
if major < 1e-6 || minor < 1e-6 {
|
||||||
let inner = first.min(second);
|
|
||||||
if inner < 1e-6 || outer - inner < 1e-6 {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let major = (outer + inner) * 0.5;
|
|
||||||
let minor = (outer - inner) * 0.5;
|
|
||||||
let center = [c.x, c.y, c.z];
|
let center = [c.x, c.y, c.z];
|
||||||
(
|
(
|
||||||
solid_model::torus_solid(center, major, minor),
|
solid_model::torus_solid(center, major, minor),
|
||||||
@@ -1985,12 +2142,40 @@ impl CadCommand for PrimitiveCommand {
|
|||||||
self.cone_step,
|
self.cone_step,
|
||||||
ConeStep::TtrFirst | ConeStep::TtrSecond { .. }
|
ConeStep::TtrFirst | ConeStep::TtrSecond { .. }
|
||||||
))
|
))
|
||||||
|
|| (self.shape == Shape::Torus
|
||||||
|
&& matches!(
|
||||||
|
self.torus_step,
|
||||||
|
TorusStep::TtrFirst | TorusStep::TtrSecond { .. }
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_tangent_point(&mut self, object: TangentObject, hit: DVec3) -> CmdResult {
|
fn on_tangent_point(&mut self, object: TangentObject, hit: DVec3) -> CmdResult {
|
||||||
if self.shape == Shape::Cone {
|
if self.shape == Shape::Cone {
|
||||||
return self.on_cone_tangent(object, hit);
|
return self.on_cone_tangent(object, hit);
|
||||||
}
|
}
|
||||||
|
if self.shape == Shape::Torus {
|
||||||
|
let object = sphere_tangent_local(object, self.plane);
|
||||||
|
let hit = self.plane.to_local(hit);
|
||||||
|
return match self.torus_step {
|
||||||
|
TorusStep::TtrFirst => {
|
||||||
|
self.torus_step = TorusStep::TtrSecond { object, hit };
|
||||||
|
CmdResult::NeedPoint
|
||||||
|
}
|
||||||
|
TorusStep::TtrSecond {
|
||||||
|
object: first,
|
||||||
|
hit: first_hit,
|
||||||
|
} => {
|
||||||
|
self.torus_step = TorusStep::TtrRadius {
|
||||||
|
first,
|
||||||
|
second: object,
|
||||||
|
first_hit,
|
||||||
|
second_hit: hit,
|
||||||
|
};
|
||||||
|
CmdResult::NeedPoint
|
||||||
|
}
|
||||||
|
_ => CmdResult::NeedPoint,
|
||||||
|
};
|
||||||
|
}
|
||||||
if self.shape != Shape::Sphere {
|
if self.shape != Shape::Sphere {
|
||||||
return self.on_point(hit);
|
return self.on_point(hit);
|
||||||
}
|
}
|
||||||
@@ -2093,13 +2278,69 @@ impl CadCommand for PrimitiveCommand {
|
|||||||
.into_owned(),
|
.into_owned(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if self.shape == Shape::Torus {
|
||||||
|
return match self.torus_step {
|
||||||
|
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
|
||||||
|
)
|
||||||
|
.into_owned(),
|
||||||
|
TorusStep::Diameter(_) => crate::tf!(
|
||||||
|
"TORUS Specify diameter <{:.4}>:",
|
||||||
|
self.torus_defaults.major_radius * 2.0
|
||||||
|
)
|
||||||
|
.into_owned(),
|
||||||
|
TorusStep::TwoPointFirst => {
|
||||||
|
t!("TORUS Specify first end point of diameter:").into_owned()
|
||||||
|
}
|
||||||
|
TorusStep::TwoPointSecond(_) => {
|
||||||
|
t!("TORUS Specify second end point of diameter:").into_owned()
|
||||||
|
}
|
||||||
|
TorusStep::ThreePointFirst => {
|
||||||
|
t!("TORUS Specify first point on torus:").into_owned()
|
||||||
|
}
|
||||||
|
TorusStep::ThreePointSecond(_) => {
|
||||||
|
t!("TORUS Specify second point on torus:").into_owned()
|
||||||
|
}
|
||||||
|
TorusStep::ThreePointThird(_, _) => {
|
||||||
|
t!("TORUS Specify third point on torus:").into_owned()
|
||||||
|
}
|
||||||
|
TorusStep::TtrFirst => {
|
||||||
|
t!("TORUS Select first tangent object:").into_owned()
|
||||||
|
}
|
||||||
|
TorusStep::TtrSecond { .. } => {
|
||||||
|
t!("TORUS Select second tangent object:").into_owned()
|
||||||
|
}
|
||||||
|
TorusStep::TtrRadius { .. } => crate::tf!(
|
||||||
|
"TORUS Specify radius <{:.4}>:",
|
||||||
|
self.torus_defaults.major_radius
|
||||||
|
)
|
||||||
|
.into_owned(),
|
||||||
|
TorusStep::TubeRadius { .. } => crate::tf!(
|
||||||
|
"TORUS Specify tube radius or [2Point/Diameter] <{:.4}>:",
|
||||||
|
self.torus_defaults.minor_radius
|
||||||
|
)
|
||||||
|
.into_owned(),
|
||||||
|
TorusStep::TubeDiameter { .. } => crate::tf!(
|
||||||
|
"TORUS Specify tube diameter <{:.4}>:",
|
||||||
|
self.torus_defaults.minor_radius * 2.0
|
||||||
|
)
|
||||||
|
.into_owned(),
|
||||||
|
TorusStep::TubeTwoPointFirst { .. } => {
|
||||||
|
t!("TORUS Specify first end point of tube diameter:").into_owned()
|
||||||
|
}
|
||||||
|
TorusStep::TubeTwoPointSecond { .. } => {
|
||||||
|
t!("TORUS Specify second end point of tube diameter:").into_owned()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
if self.height_step {
|
if self.height_step {
|
||||||
return t!("%{n} Specify height <Enter for default>:", n = n).into_owned();
|
return t!("%{n} Specify height <Enter for default>:", n = n).into_owned();
|
||||||
}
|
}
|
||||||
match (self.shape, self.pts.len()) {
|
match (self.shape, self.pts.len()) {
|
||||||
(Shape::Torus, 0) => t!("%{n} Specify center point:", n = n).into_owned(),
|
|
||||||
(Shape::Torus, 1) => t!("%{n} Specify outer radius:", n = n).into_owned(),
|
|
||||||
(Shape::Torus, _) => t!("%{n} Specify inner radius:", n = n).into_owned(),
|
|
||||||
(shape, 0) if shape.radial() => {
|
(shape, 0) if shape.radial() => {
|
||||||
t!("%{n} Specify center point:", n = n).into_owned()
|
t!("%{n} Specify center point:", n = n).into_owned()
|
||||||
}
|
}
|
||||||
@@ -2123,6 +2364,23 @@ impl CadCommand for PrimitiveCommand {
|
|||||||
_ => Vec::new(),
|
_ => Vec::new(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if self.shape == Shape::Torus {
|
||||||
|
return match self.torus_step {
|
||||||
|
TorusStep::Center => vec![
|
||||||
|
CmdOption::new("3P", "3P"),
|
||||||
|
CmdOption::new("2P", "2P"),
|
||||||
|
CmdOption::new("Ttr", "T"),
|
||||||
|
],
|
||||||
|
TorusStep::Radius(_) => {
|
||||||
|
vec![CmdOption::new(t!("Diameter").as_ref(), "D")]
|
||||||
|
}
|
||||||
|
TorusStep::TubeRadius { .. } => vec![
|
||||||
|
CmdOption::new(t!("2Point").as_ref(), "2P"),
|
||||||
|
CmdOption::new(t!("Diameter").as_ref(), "D"),
|
||||||
|
],
|
||||||
|
_ => Vec::new(),
|
||||||
|
};
|
||||||
|
}
|
||||||
if self.shape == Shape::Cone {
|
if self.shape == Shape::Cone {
|
||||||
return self.cone_options();
|
return self.cone_options();
|
||||||
}
|
}
|
||||||
@@ -2187,6 +2445,76 @@ impl CadCommand for PrimitiveCommand {
|
|||||||
SphereStep::TtrFirst | SphereStep::TtrSecond { .. } => CmdResult::NeedPoint,
|
SphereStep::TtrFirst | SphereStep::TtrSecond { .. } => CmdResult::NeedPoint,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if self.shape == Shape::Torus {
|
||||||
|
let local = self.plane.to_local(pt);
|
||||||
|
if !local.is_finite() {
|
||||||
|
return CmdResult::NeedPoint;
|
||||||
|
}
|
||||||
|
return match self.torus_step {
|
||||||
|
TorusStep::Center => {
|
||||||
|
self.torus_step = TorusStep::Radius(local);
|
||||||
|
CmdResult::NeedPoint
|
||||||
|
}
|
||||||
|
TorusStep::Radius(center) => {
|
||||||
|
self.set_torus_major_radius(center, center.distance(local))
|
||||||
|
}
|
||||||
|
TorusStep::Diameter(center) => {
|
||||||
|
self.set_torus_major_radius(center, center.distance(local) * 0.5)
|
||||||
|
}
|
||||||
|
TorusStep::TwoPointFirst => {
|
||||||
|
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::ThreePointFirst => {
|
||||||
|
self.torus_step = TorusStep::ThreePointSecond(local);
|
||||||
|
CmdResult::NeedPoint
|
||||||
|
}
|
||||||
|
TorusStep::ThreePointSecond(first) => {
|
||||||
|
self.torus_step = TorusStep::ThreePointThird(first, local);
|
||||||
|
CmdResult::NeedPoint
|
||||||
|
}
|
||||||
|
TorusStep::ThreePointThird(first, second) => {
|
||||||
|
let Some((center, radius)) =
|
||||||
|
sphere_through_three_points(first, second, local)
|
||||||
|
else {
|
||||||
|
return CmdResult::NeedPoint;
|
||||||
|
};
|
||||||
|
self.set_torus_major_radius(center, radius)
|
||||||
|
}
|
||||||
|
TorusStep::TtrRadius { second_hit, .. } => {
|
||||||
|
self.torus_ttr_result(second_hit.distance(local))
|
||||||
|
}
|
||||||
|
TorusStep::TubeRadius {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
} => self.commit_torus(center, major_radius, center.distance(local)),
|
||||||
|
TorusStep::TubeDiameter {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
} => self.commit_torus(center, major_radius, center.distance(local) * 0.5),
|
||||||
|
TorusStep::TubeTwoPointFirst {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
} => {
|
||||||
|
self.torus_step = TorusStep::TubeTwoPointSecond {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
first: local,
|
||||||
|
};
|
||||||
|
CmdResult::NeedPoint
|
||||||
|
}
|
||||||
|
TorusStep::TubeTwoPointSecond {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
first,
|
||||||
|
} => self.commit_torus(center, major_radius, first.distance(local) * 0.5),
|
||||||
|
TorusStep::TtrFirst | TorusStep::TtrSecond { .. } => CmdResult::NeedPoint,
|
||||||
|
};
|
||||||
|
}
|
||||||
if self.shape == Shape::Cone {
|
if self.shape == Shape::Cone {
|
||||||
return self.on_cone_point(pt);
|
return self.on_cone_point(pt);
|
||||||
}
|
}
|
||||||
@@ -2297,6 +2625,25 @@ impl CadCommand for PrimitiveCommand {
|
|||||||
_ => CmdResult::Cancel,
|
_ => CmdResult::Cancel,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if self.shape == Shape::Torus {
|
||||||
|
return match self.torus_step {
|
||||||
|
TorusStep::Radius(center) | TorusStep::Diameter(center) => {
|
||||||
|
self.set_torus_major_radius(center, self.torus_defaults.major_radius)
|
||||||
|
}
|
||||||
|
TorusStep::TtrRadius { .. } => {
|
||||||
|
self.torus_ttr_result(self.torus_defaults.major_radius)
|
||||||
|
}
|
||||||
|
TorusStep::TubeRadius {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
}
|
||||||
|
| TorusStep::TubeDiameter {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
} => self.commit_torus(center, major_radius, self.torus_defaults.minor_radius),
|
||||||
|
_ => CmdResult::Cancel,
|
||||||
|
};
|
||||||
|
}
|
||||||
if self.shape == Shape::Cone {
|
if self.shape == Shape::Cone {
|
||||||
return match self.cone_step {
|
return match self.cone_step {
|
||||||
ConeStep::BaseRadius | ConeStep::BaseDiameter => {
|
ConeStep::BaseRadius | ConeStep::BaseDiameter => {
|
||||||
@@ -2393,6 +2740,17 @@ impl CadCommand for PrimitiveCommand {
|
|||||||
| SphereStep::TtrRadius { .. }
|
| SphereStep::TtrRadius { .. }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if self.shape == Shape::Torus {
|
||||||
|
return matches!(
|
||||||
|
self.torus_step,
|
||||||
|
TorusStep::Center
|
||||||
|
| TorusStep::Radius(_)
|
||||||
|
| TorusStep::Diameter(_)
|
||||||
|
| TorusStep::TtrRadius { .. }
|
||||||
|
| TorusStep::TubeRadius { .. }
|
||||||
|
| TorusStep::TubeDiameter { .. }
|
||||||
|
);
|
||||||
|
}
|
||||||
if self.shape == Shape::Cone {
|
if self.shape == Shape::Cone {
|
||||||
return matches!(
|
return matches!(
|
||||||
self.cone_step,
|
self.cone_step,
|
||||||
@@ -2437,6 +2795,12 @@ impl CadCommand for PrimitiveCommand {
|
|||||||
if self.shape == Shape::Sphere {
|
if self.shape == Shape::Sphere {
|
||||||
return matches!(self.sphere_step, SphereStep::Center | SphereStep::Radius(_));
|
return matches!(self.sphere_step, SphereStep::Center | SphereStep::Radius(_));
|
||||||
}
|
}
|
||||||
|
if self.shape == Shape::Torus {
|
||||||
|
return matches!(
|
||||||
|
self.torus_step,
|
||||||
|
TorusStep::Center | TorusStep::Radius(_) | TorusStep::TubeRadius { .. }
|
||||||
|
);
|
||||||
|
}
|
||||||
if self.shape == Shape::Cone {
|
if self.shape == Shape::Cone {
|
||||||
return matches!(
|
return matches!(
|
||||||
self.cone_step,
|
self.cone_step,
|
||||||
@@ -2502,6 +2866,75 @@ impl CadCommand for PrimitiveCommand {
|
|||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if self.shape == Shape::Torus {
|
||||||
|
let token = raw.trim();
|
||||||
|
let upper = token.to_uppercase();
|
||||||
|
return match self.torus_step {
|
||||||
|
TorusStep::Center if upper == "3P" => {
|
||||||
|
self.torus_step = TorusStep::ThreePointFirst;
|
||||||
|
Some(CmdResult::NeedPoint)
|
||||||
|
}
|
||||||
|
TorusStep::Center if upper == "2P" => {
|
||||||
|
self.torus_step = TorusStep::TwoPointFirst;
|
||||||
|
Some(CmdResult::NeedPoint)
|
||||||
|
}
|
||||||
|
TorusStep::Center if matches!(upper.as_str(), "T" | "TTR") => {
|
||||||
|
self.torus_step = TorusStep::TtrFirst;
|
||||||
|
Some(CmdResult::NeedPoint)
|
||||||
|
}
|
||||||
|
TorusStep::Radius(center) if matches!(upper.as_str(), "D" | "DIAMETER") => {
|
||||||
|
self.torus_step = TorusStep::Diameter(center);
|
||||||
|
Some(CmdResult::NeedPoint)
|
||||||
|
}
|
||||||
|
TorusStep::Radius(center) => {
|
||||||
|
let radius = crate::entities::common::parse_typed_length(token)?;
|
||||||
|
Some(self.set_torus_major_radius(center, radius))
|
||||||
|
}
|
||||||
|
TorusStep::Diameter(center) => {
|
||||||
|
let diameter = crate::entities::common::parse_typed_length(token)?;
|
||||||
|
Some(self.set_torus_major_radius(center, diameter * 0.5))
|
||||||
|
}
|
||||||
|
TorusStep::TtrRadius { .. } => {
|
||||||
|
let radius = crate::entities::common::parse_typed_length(token)?;
|
||||||
|
Some(self.torus_ttr_result(radius))
|
||||||
|
}
|
||||||
|
TorusStep::TubeRadius {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
} if matches!(upper.as_str(), "2P" | "2POINT") => {
|
||||||
|
self.torus_step = TorusStep::TubeTwoPointFirst {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
};
|
||||||
|
Some(CmdResult::NeedPoint)
|
||||||
|
}
|
||||||
|
TorusStep::TubeRadius {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
} if matches!(upper.as_str(), "D" | "DIAMETER") => {
|
||||||
|
self.torus_step = TorusStep::TubeDiameter {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
};
|
||||||
|
Some(CmdResult::NeedPoint)
|
||||||
|
}
|
||||||
|
TorusStep::TubeRadius {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
} => {
|
||||||
|
let radius = crate::entities::common::parse_typed_length(token)?;
|
||||||
|
Some(self.commit_torus(center, major_radius, radius))
|
||||||
|
}
|
||||||
|
TorusStep::TubeDiameter {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
} => {
|
||||||
|
let diameter = crate::entities::common::parse_typed_length(token)?;
|
||||||
|
Some(self.commit_torus(center, major_radius, diameter * 0.5))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
if self.shape == Shape::Cone {
|
if self.shape == Shape::Cone {
|
||||||
return self.on_cone_text(raw);
|
return self.on_cone_text(raw);
|
||||||
}
|
}
|
||||||
@@ -2556,6 +2989,63 @@ impl CadCommand for PrimitiveCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> {
|
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> {
|
||||||
|
if self.shape == Shape::Torus {
|
||||||
|
let local = self.plane.to_local(pt);
|
||||||
|
if !local.is_finite() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
return match self.torus_step {
|
||||||
|
TorusStep::Radius(center) => self.torus_preview(
|
||||||
|
center,
|
||||||
|
center.distance(local),
|
||||||
|
self.torus_defaults.minor_radius,
|
||||||
|
),
|
||||||
|
TorusStep::Diameter(center) => self.torus_preview(
|
||||||
|
center,
|
||||||
|
center.distance(local) * 0.5,
|
||||||
|
self.torus_defaults.minor_radius,
|
||||||
|
),
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
TorusStep::TtrRadius {
|
||||||
|
first,
|
||||||
|
second,
|
||||||
|
first_hit,
|
||||||
|
second_hit,
|
||||||
|
} => {
|
||||||
|
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,
|
||||||
|
)?;
|
||||||
|
self.torus_preview(center, radius, self.torus_defaults.minor_radius)
|
||||||
|
}
|
||||||
|
TorusStep::TubeRadius {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
} => self.torus_preview(center, major_radius, center.distance(local)),
|
||||||
|
TorusStep::TubeDiameter {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
} => self.torus_preview(center, major_radius, center.distance(local) * 0.5),
|
||||||
|
TorusStep::TubeTwoPointSecond {
|
||||||
|
center,
|
||||||
|
major_radius,
|
||||||
|
first,
|
||||||
|
} => self.torus_preview(center, major_radius, first.distance(local) * 0.5),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
if self.shape == Shape::Sphere {
|
if self.shape == Shape::Sphere {
|
||||||
let local = self.plane.to_local(pt);
|
let local = self.plane.to_local(pt);
|
||||||
if !local.is_finite() {
|
if !local.is_finite() {
|
||||||
@@ -2706,6 +3196,25 @@ impl CadCommand for PrimitiveCommand {
|
|||||||
fn dyn_spec(&self) -> Option<crate::command::DynSpec> {
|
fn dyn_spec(&self) -> Option<crate::command::DynSpec> {
|
||||||
use crate::command::{DynAnchor, DynFieldSpec, DynGuide, DynRole, DynSpec};
|
use crate::command::{DynAnchor, DynFieldSpec, DynGuide, DynRole, DynSpec};
|
||||||
|
|
||||||
|
if self.shape == Shape::Torus {
|
||||||
|
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::TubeRadius { center, .. } => (center, DynRole::Radius),
|
||||||
|
TorusStep::TubeDiameter { center, .. } => (center, DynRole::Diameter),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
return Some(DynSpec {
|
||||||
|
anchor: DynAnchor::Point(self.plane.to_world(anchor)),
|
||||||
|
fields: vec![DynFieldSpec::new(role)],
|
||||||
|
guide: DynGuide::Radius,
|
||||||
|
ref_point: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if self.shape == Shape::Sphere {
|
if self.shape == Shape::Sphere {
|
||||||
let (anchor, role) = match self.sphere_step {
|
let (anchor, role) = match self.sphere_step {
|
||||||
SphereStep::Radius(center) => (center, DynRole::Radius),
|
SphereStep::Radius(center) => (center, DynRole::Radius),
|
||||||
@@ -2799,6 +3308,21 @@ impl CadCommand for PrimitiveCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn dyn_live_value(&self, cursor: DVec3) -> Option<f64> {
|
fn dyn_live_value(&self, cursor: DVec3) -> Option<f64> {
|
||||||
|
if self.shape == Shape::Torus {
|
||||||
|
let local = self.plane.to_local(cursor);
|
||||||
|
return match self.torus_step {
|
||||||
|
TorusStep::Radius(center) | TorusStep::TubeRadius { center, .. } => {
|
||||||
|
Some(center.distance(local))
|
||||||
|
}
|
||||||
|
TorusStep::Diameter(center) | TorusStep::TubeDiameter { center, .. } => {
|
||||||
|
Some(center.distance(local))
|
||||||
|
}
|
||||||
|
TorusStep::TtrRadius { second_hit, .. } => {
|
||||||
|
Some(second_hit.distance(local))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
if self.shape == Shape::Sphere {
|
if self.shape == Shape::Sphere {
|
||||||
let local = self.plane.to_local(cursor);
|
let local = self.plane.to_local(cursor);
|
||||||
return match self.sphere_step {
|
return match self.sphere_step {
|
||||||
@@ -2882,6 +3406,48 @@ impl CadCommand for PrimitiveCommand {
|
|||||||
|
|
||||||
// ── Footprint preview ───────────────────────────────────────────────────────
|
// ── Footprint preview ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
fn torus_wire(center: DVec3, major_radius: f64, minor_radius: f64) -> WireModel {
|
||||||
|
let mut points = Vec::new();
|
||||||
|
let profile_limit = if minor_radius > major_radius {
|
||||||
|
std::f64::consts::PI - (major_radius / minor_radius).acos()
|
||||||
|
} else {
|
||||||
|
std::f64::consts::PI
|
||||||
|
};
|
||||||
|
|
||||||
|
for fraction in [-0.75_f64, -0.5, 0.0, 0.5, 0.75] {
|
||||||
|
let angle = profile_limit * fraction;
|
||||||
|
let ring_radius = major_radius + minor_radius * angle.cos();
|
||||||
|
if ring_radius <= 1e-9 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if !points.is_empty() {
|
||||||
|
points.push([f32::NAN; 3]);
|
||||||
|
}
|
||||||
|
circle_points(
|
||||||
|
&mut points,
|
||||||
|
center + DVec3::Z * (minor_radius * angle.sin()),
|
||||||
|
ring_radius,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for meridian in 0..12 {
|
||||||
|
points.push([f32::NAN; 3]);
|
||||||
|
let around = meridian as f64 * std::f64::consts::TAU / 12.0;
|
||||||
|
for sample in 0..=32 {
|
||||||
|
let profile = -profile_limit + 2.0 * profile_limit * sample as f64 / 32.0;
|
||||||
|
let radial = (major_radius + minor_radius * profile.cos()).max(0.0);
|
||||||
|
let point = center
|
||||||
|
+ DVec3::new(
|
||||||
|
radial * around.cos(),
|
||||||
|
radial * around.sin(),
|
||||||
|
minor_radius * profile.sin(),
|
||||||
|
);
|
||||||
|
points.push(point.as_vec3().to_array());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wire("torus_preview", points)
|
||||||
|
}
|
||||||
|
|
||||||
fn footprint_wire(shape: Shape, pts: &[DVec3]) -> WireModel {
|
fn footprint_wire(shape: Shape, pts: &[DVec3]) -> WireModel {
|
||||||
let mut points: Vec<[f32; 3]> = Vec::new();
|
let mut points: Vec<[f32; 3]> = Vec::new();
|
||||||
if shape == Shape::Pyramid {
|
if shape == Shape::Pyramid {
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ pub fn has_specialized_primitive_properties(
|
|||||||
| SolidHistoryOperation::Sphere(_)
|
| SolidHistoryOperation::Sphere(_)
|
||||||
| SolidHistoryOperation::Cone(_)
|
| SolidHistoryOperation::Cone(_)
|
||||||
| SolidHistoryOperation::Cylinder(_)
|
| SolidHistoryOperation::Cylinder(_)
|
||||||
|
| SolidHistoryOperation::Torus(_)
|
||||||
| SolidHistoryOperation::Pyramid(_)
|
| SolidHistoryOperation::Pyramid(_)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -117,11 +118,81 @@ pub fn reference_point(operation: &SolidHistoryOperation) -> Option<glam::DVec3>
|
|||||||
}
|
}
|
||||||
SolidHistoryOperation::Cone(value) => world_point(value.base.transform, [0.0; 3]),
|
SolidHistoryOperation::Cone(value) => world_point(value.base.transform, [0.0; 3]),
|
||||||
SolidHistoryOperation::Cylinder(value) => world_point(value.base.transform, [0.0; 3]),
|
SolidHistoryOperation::Cylinder(value) => world_point(value.base.transform, [0.0; 3]),
|
||||||
|
SolidHistoryOperation::Torus(value) => world_point(value.base.transform, [0.0; 3]),
|
||||||
SolidHistoryOperation::Pyramid(value) => world_point(value.base.transform, [0.0; 3]),
|
SolidHistoryOperation::Pyramid(value) => world_point(value.base.transform, [0.0; 3]),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn torus_properties(
|
||||||
|
document: &acadrust::CadDocument,
|
||||||
|
handle: acadrust::Handle,
|
||||||
|
value: &SolidHistoryTorus,
|
||||||
|
) -> Vec<PropSection> {
|
||||||
|
let Some(position) = world_point(value.base.transform, [0.0; 3]) else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
let (record_history, object_show_history, show_history_mode) =
|
||||||
|
history_flags(document, handle).unwrap_or((false, false, 1));
|
||||||
|
let (show_history, _) = displayed_history_state(object_show_history, show_history_mode);
|
||||||
|
vec![
|
||||||
|
PropSection {
|
||||||
|
title: t!("Geometry").into_owned(),
|
||||||
|
props: vec![
|
||||||
|
Property {
|
||||||
|
label: t!("Solid type").into_owned(),
|
||||||
|
field: "solid_history_type",
|
||||||
|
value: PropValue::ReadOnly(t!("Torus").into_owned()),
|
||||||
|
},
|
||||||
|
crate::entities::common::edit_prop(
|
||||||
|
t!("Position X").as_ref(),
|
||||||
|
PROP_POSITION_X,
|
||||||
|
position.x,
|
||||||
|
),
|
||||||
|
crate::entities::common::edit_prop(
|
||||||
|
t!("Position Y").as_ref(),
|
||||||
|
PROP_POSITION_Y,
|
||||||
|
position.y,
|
||||||
|
),
|
||||||
|
crate::entities::common::edit_prop(
|
||||||
|
t!("Position Z").as_ref(),
|
||||||
|
PROP_POSITION_Z,
|
||||||
|
position.z,
|
||||||
|
),
|
||||||
|
history_prop(
|
||||||
|
t!("Torus radius").as_ref(),
|
||||||
|
PROP_MAJOR_RADIUS,
|
||||||
|
value.major_radius,
|
||||||
|
),
|
||||||
|
history_prop(
|
||||||
|
t!("Tube radius").as_ref(),
|
||||||
|
PROP_MINOR_RADIUS,
|
||||||
|
value.minor_radius,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
PropSection {
|
||||||
|
title: t!("Solid History").into_owned(),
|
||||||
|
props: vec![
|
||||||
|
Property {
|
||||||
|
label: t!("History").into_owned(),
|
||||||
|
field: PROP_HISTORY,
|
||||||
|
value: PropValue::ReadOnly(
|
||||||
|
if record_history { "Record" } else { "None" }.to_string(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Property {
|
||||||
|
label: t!("Show History").into_owned(),
|
||||||
|
field: PROP_SHOW_HISTORY,
|
||||||
|
value: PropValue::ReadOnly(
|
||||||
|
if show_history { "Yes" } else { "No" }.to_string(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
fn cone_properties(
|
fn cone_properties(
|
||||||
document: &acadrust::CadDocument,
|
document: &acadrust::CadDocument,
|
||||||
handle: acadrust::Handle,
|
handle: acadrust::Handle,
|
||||||
@@ -674,41 +745,20 @@ pub fn primitive_properties(
|
|||||||
let Some(operation) = document.solid_history_operation(handle) else {
|
let Some(operation) = document.solid_history_operation(handle) else {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
};
|
};
|
||||||
let props = match operation {
|
match operation {
|
||||||
SolidHistoryOperation::Box(value) => {
|
SolidHistoryOperation::Box(value) => {
|
||||||
return rectangular_properties(document, handle, value, "Box")
|
rectangular_properties(document, handle, value, "Box")
|
||||||
}
|
}
|
||||||
SolidHistoryOperation::Wedge(value) => {
|
SolidHistoryOperation::Wedge(value) => {
|
||||||
return rectangular_properties(document, handle, value, "Wedge")
|
rectangular_properties(document, handle, value, "Wedge")
|
||||||
}
|
}
|
||||||
SolidHistoryOperation::Sphere(value) => {
|
SolidHistoryOperation::Sphere(value) => sphere_properties(document, handle, value),
|
||||||
return sphere_properties(document, handle, value)
|
SolidHistoryOperation::Cone(value) => cone_properties(document, handle, value),
|
||||||
}
|
SolidHistoryOperation::Cylinder(value) => cylinder_properties(document, handle, value),
|
||||||
SolidHistoryOperation::Cone(value) => return cone_properties(document, handle, value),
|
SolidHistoryOperation::Pyramid(value) => pyramid_properties(document, handle, value),
|
||||||
SolidHistoryOperation::Cylinder(value) => {
|
SolidHistoryOperation::Torus(value) => torus_properties(document, handle, value),
|
||||||
return cylinder_properties(document, handle, value)
|
_ => Vec::new(),
|
||||||
}
|
}
|
||||||
SolidHistoryOperation::Pyramid(value) => {
|
|
||||||
return pyramid_properties(document, handle, value)
|
|
||||||
}
|
|
||||||
SolidHistoryOperation::Torus(value) => vec![
|
|
||||||
history_prop(
|
|
||||||
t!("Outer Radius").as_ref(),
|
|
||||||
PROP_OUTER_RADIUS,
|
|
||||||
value.major_radius + value.minor_radius,
|
|
||||||
),
|
|
||||||
history_prop(
|
|
||||||
t!("Inner Radius").as_ref(),
|
|
||||||
PROP_INNER_RADIUS,
|
|
||||||
(value.major_radius - value.minor_radius).max(0.0),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
_ => return Vec::new(),
|
|
||||||
};
|
|
||||||
vec![PropSection {
|
|
||||||
title: t!("Primitive").into_owned(),
|
|
||||||
props,
|
|
||||||
}]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_primitive_property(field: &str) -> bool {
|
pub fn is_primitive_property(field: &str) -> bool {
|
||||||
@@ -941,6 +991,36 @@ fn apply_cylinder_position_property(
|
|||||||
Some(true)
|
Some(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_torus_position_property(
|
||||||
|
value: &mut SolidHistoryTorus,
|
||||||
|
field: &str,
|
||||||
|
text: &str,
|
||||||
|
) -> Option<bool> {
|
||||||
|
let axis = match field {
|
||||||
|
PROP_POSITION_X => 0,
|
||||||
|
PROP_POSITION_Y => 1,
|
||||||
|
PROP_POSITION_Z => 2,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
let target = crate::entities::common::parse_length(text)?;
|
||||||
|
if !target.is_finite() {
|
||||||
|
return Some(false);
|
||||||
|
}
|
||||||
|
let current = matrix(value.base.transform)?;
|
||||||
|
let origin = current.transform_point3(glam::DVec3::ZERO);
|
||||||
|
if !origin.is_finite() || (target - origin[axis]).abs() <= 1e-12 {
|
||||||
|
return Some(false);
|
||||||
|
}
|
||||||
|
let mut delta = glam::DVec3::ZERO;
|
||||||
|
delta[axis] = target - origin[axis];
|
||||||
|
let updated = glam::DMat4::from_translation(delta) * current;
|
||||||
|
if !updated.is_finite() || updated.determinant().abs() <= 1e-12 {
|
||||||
|
return Some(false);
|
||||||
|
}
|
||||||
|
value.base.transform = updated.to_cols_array();
|
||||||
|
Some(true)
|
||||||
|
}
|
||||||
|
|
||||||
fn canonicalize_cylinder_radii(value: &mut SolidHistoryCylinder) -> bool {
|
fn canonicalize_cylinder_radii(value: &mut SolidHistoryCylinder) -> bool {
|
||||||
if value.minor_radius <= value.major_radius {
|
if value.minor_radius <= value.major_radius {
|
||||||
value.x_radius = value.major_radius;
|
value.x_radius = value.major_radius;
|
||||||
@@ -1099,6 +1179,11 @@ pub fn apply_primitive_property(
|
|||||||
return applied;
|
return applied;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let SolidHistoryOperation::Torus(torus_value) = operation {
|
||||||
|
if let Some(applied) = apply_torus_position_property(torus_value, field, value) {
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
}
|
||||||
if let SolidHistoryOperation::Pyramid(pyramid_value) = operation {
|
if let SolidHistoryOperation::Pyramid(pyramid_value) = operation {
|
||||||
if let Some(applied) = apply_pyramid_geometry_property(pyramid_value, field, value) {
|
if let Some(applied) = apply_pyramid_geometry_property(pyramid_value, field, value) {
|
||||||
return applied;
|
return applied;
|
||||||
@@ -1228,33 +1313,27 @@ pub fn apply_primitive_property(
|
|||||||
}
|
}
|
||||||
value.radius = radius;
|
value.radius = radius;
|
||||||
}
|
}
|
||||||
SolidHistoryOperation::Torus(value) => {
|
SolidHistoryOperation::Torus(value) => match field {
|
||||||
let outer = value.major_radius + value.minor_radius;
|
PROP_MAJOR_RADIUS => {
|
||||||
let inner = (value.major_radius - value.minor_radius).max(0.0);
|
let Some(radius) = positive() else {
|
||||||
match field {
|
return false;
|
||||||
PROP_OUTER_RADIUS => {
|
};
|
||||||
let Some(next_outer) = positive() else {
|
if (radius - value.major_radius).abs() <= 1e-12 {
|
||||||
return false;
|
return false;
|
||||||
};
|
|
||||||
if next_outer <= inner {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
value.major_radius = (next_outer + inner) * 0.5;
|
|
||||||
value.minor_radius = (next_outer - inner) * 0.5;
|
|
||||||
}
|
}
|
||||||
PROP_INNER_RADIUS => {
|
value.major_radius = radius;
|
||||||
let Some(next_inner) = positive() else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
if next_inner >= outer {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
value.major_radius = (outer + next_inner) * 0.5;
|
|
||||||
value.minor_radius = (outer - next_inner) * 0.5;
|
|
||||||
}
|
|
||||||
_ => return false,
|
|
||||||
}
|
}
|
||||||
}
|
PROP_MINOR_RADIUS => {
|
||||||
|
let Some(radius) = positive() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (radius - value.minor_radius).abs() <= 1e-12 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value.minor_radius = radius;
|
||||||
|
}
|
||||||
|
_ => return false,
|
||||||
|
},
|
||||||
SolidHistoryOperation::Pyramid(value) => match field {
|
SolidHistoryOperation::Pyramid(value) => match field {
|
||||||
PROP_BASE_RADIUS => {
|
PROP_BASE_RADIUS => {
|
||||||
let Some(displayed) = positive() else {
|
let Some(displayed) = positive() else {
|
||||||
@@ -1555,22 +1634,18 @@ pub fn primitive_grips(
|
|||||||
),
|
),
|
||||||
SolidHistoryOperation::Torus(value) => {
|
SolidHistoryOperation::Torus(value) => {
|
||||||
add(
|
add(
|
||||||
GRIP_OUTER_RADIUS,
|
GRIP_MAJOR_RADIUS,
|
||||||
value.base.transform,
|
value.base.transform,
|
||||||
[value.major_radius + value.minor_radius, 0.0, 0.0],
|
[value.major_radius, 0.0, 0.0],
|
||||||
GripShape::Square,
|
GripShape::Square,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
add(
|
add(
|
||||||
GRIP_INNER_RADIUS,
|
GRIP_MINOR_RADIUS,
|
||||||
value.base.transform,
|
value.base.transform,
|
||||||
[
|
[value.major_radius, 0.0, value.minor_radius],
|
||||||
(value.major_radius - value.minor_radius).max(0.0),
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
],
|
|
||||||
GripShape::Square,
|
GripShape::Square,
|
||||||
None,
|
Some([0.0, 0.0, 1.0]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
SolidHistoryOperation::Pyramid(value) => {
|
SolidHistoryOperation::Pyramid(value) => {
|
||||||
@@ -1728,24 +1803,8 @@ pub fn apply_primitive_grip(
|
|||||||
value.radius = local.length().max(1e-6);
|
value.radius = local.length().max(1e-6);
|
||||||
}
|
}
|
||||||
SolidHistoryOperation::Torus(value) => match grip_id {
|
SolidHistoryOperation::Torus(value) => match grip_id {
|
||||||
GRIP_OUTER_RADIUS => {
|
GRIP_MAJOR_RADIUS => value.major_radius = local.x.hypot(local.y).max(1e-6),
|
||||||
let outer = local.x.hypot(local.y).max(1e-6);
|
GRIP_MINOR_RADIUS => value.minor_radius = local.z.abs().max(1e-6),
|
||||||
let inner = (value.major_radius - value.minor_radius).max(1e-6);
|
|
||||||
if outer <= inner {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
value.major_radius = (outer + inner) * 0.5;
|
|
||||||
value.minor_radius = (outer - inner) * 0.5;
|
|
||||||
}
|
|
||||||
GRIP_INNER_RADIUS => {
|
|
||||||
let inner = local.x.hypot(local.y).max(1e-6);
|
|
||||||
let outer = value.major_radius + value.minor_radius;
|
|
||||||
if inner >= outer {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
value.major_radius = (outer + inner) * 0.5;
|
|
||||||
value.minor_radius = (outer - inner) * 0.5;
|
|
||||||
}
|
|
||||||
_ => return false,
|
_ => return false,
|
||||||
},
|
},
|
||||||
SolidHistoryOperation::Pyramid(value) => match grip_id {
|
SolidHistoryOperation::Pyramid(value) => match grip_id {
|
||||||
|
|||||||
Reference in new issue
Block a user