forked from 1b-agent/opencadstudio-upstream
feat(model): complete cone workflow
This commit is contained in:
commit
72669f58f3
11 files changed
+1217
-66
No files matched your search
Generated
+2
-2
@@ -72,7 +72,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
|
||||
[[package]]
|
||||
name = "acadrust"
|
||||
version = "0.4.1"
|
||||
source = "git+https://[email protected]/HakanSeven12/cadcodec.git?rev=45789a7700bd5c006feb4c7ff16c2ff80a7047db#45789a7700bd5c006feb4c7ff16c2ff80a7047db"
|
||||
source = "git+https://[email protected]/HakanSeven12/cadcodec.git?rev=3a87743ad7b0fc74f0137ccd48f598116e2904b0#3a87743ad7b0fc74f0137ccd48f598116e2904b0"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"anyhow",
|
||||
@@ -878,7 +878,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
||||
[[package]]
|
||||
name = "cadkernel"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=0d48aa453f8689e4d737201b0a79604974427c6f#0d48aa453f8689e4d737201b0a79604974427c6f"
|
||||
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=48c634995fa60d3928c37bae49b12b421c56c886#48c634995fa60d3928c37bae49b12b421c56c886"
|
||||
dependencies = [
|
||||
"acadrust",
|
||||
"cavalier_contours",
|
||||
|
||||
+4
-3
@@ -27,8 +27,8 @@ glam = { version = "0.33", features = ["bytemuck"] }
|
||||
rfd = "0.17"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
env_logger = "0.11"
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "45789a7700bd5c006feb4c7ff16c2ff80a7047db", features = ["serde"] }
|
||||
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "0d48aa453f8689e4d737201b0a79604974427c6f", features = ["acis", "offset"] }
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "3a87743ad7b0fc74f0137ccd48f598116e2904b0", features = ["serde"] }
|
||||
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "48c634995fa60d3928c37bae49b12b421c56c886", features = ["acis", "offset"] }
|
||||
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
|
||||
flate2 = "1"
|
||||
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
|
||||
@@ -61,7 +61,8 @@ iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aa
|
||||
iced_widget = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" }
|
||||
|
||||
[patch."https://github.com/HakanSeven12/cadcodec.git"]
|
||||
acadrust = { git = "https://[email protected]/HakanSeven12/cadcodec.git", rev = "45789a7700bd5c006feb4c7ff16c2ff80a7047db" }
|
||||
acadrust = { git = "https://[email protected]/HakanSeven12/cadcodec.git", rev = "3a87743ad7b0fc74f0137ccd48f598116e2904b0" }
|
||||
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
ocs_plugin_api = { path = "crates/ocs_plugin_api", features = ["host"] }
|
||||
|
||||
@@ -14,7 +14,7 @@ serde = { version = "1", features = ["derive"] }
|
||||
# Pulled in only by the `host` feature, which adds the `acadrust`-typed
|
||||
# `HostApi` runtime surface. The default crate stays dependency-free so engine
|
||||
# crates and external tooling can depend on the manifest/ribbon contract cheaply.
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "5b2ae66", optional = true, features = ["serde"] }
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "3a87743ad7b0fc74f0137ccd48f598116e2904b0", optional = true, features = ["serde"] }
|
||||
|
||||
# Runtime IPC and serialization (host feature only).
|
||||
interprocess = { version = "2", optional = true }
|
||||
@@ -37,7 +37,7 @@ serde_json = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
cargo-lock = "11"
|
||||
# acadrust is scanned at build time to generate the embedded type registry.
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "5b2ae66", features = ["serde"] }
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "3a87743ad7b0fc74f0137ccd48f598116e2904b0", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1"
|
||||
@@ -8,7 +8,7 @@ publish = false
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "5b2ae66", features = ["serde"] }
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "3a87743ad7b0fc74f0137ccd48f598116e2904b0", features = ["serde"] }
|
||||
bincode = "1.3"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
console_error_panic_hook = "0.1"
|
||||
|
||||
@@ -151,7 +151,8 @@ fn history_step_id(operation: &SolidHistoryOperation) -> Option<i32> {
|
||||
SolidHistoryOperation::Unknown => return None,
|
||||
SolidHistoryOperation::Box(value) | SolidHistoryOperation::Wedge(value) => &value.base,
|
||||
SolidHistoryOperation::Sphere(value) => &value.base,
|
||||
SolidHistoryOperation::Cylinder(value) | SolidHistoryOperation::Cone(value) => &value.base,
|
||||
SolidHistoryOperation::Cylinder(value) => &value.base,
|
||||
SolidHistoryOperation::Cone(value) => &value.base,
|
||||
SolidHistoryOperation::Pyramid(value) => &value.base,
|
||||
SolidHistoryOperation::Torus(value) => &value.base,
|
||||
SolidHistoryOperation::Boolean(value) => &value.base,
|
||||
@@ -225,7 +226,7 @@ fn history_operation_text(operation: &SolidHistoryOperation) -> String {
|
||||
history_base_text(&value.base),
|
||||
value.radius
|
||||
),
|
||||
SolidHistoryOperation::Cylinder(value) | SolidHistoryOperation::Cone(value) => format!(
|
||||
SolidHistoryOperation::Cylinder(value) => format!(
|
||||
"{}; height {:.6}; radii {:.6}/{:.6}; x-radius {:.6}",
|
||||
history_base_text(&value.base),
|
||||
value.height,
|
||||
@@ -233,6 +234,14 @@ fn history_operation_text(operation: &SolidHistoryOperation) -> String {
|
||||
value.minor_radius,
|
||||
value.x_radius
|
||||
),
|
||||
SolidHistoryOperation::Cone(value) => format!(
|
||||
"{}; height {:.6}; base radii {:.6}/{:.6}; top radius {:.6}",
|
||||
history_base_text(&value.base),
|
||||
value.height,
|
||||
value.base_x_radius,
|
||||
value.base_y_radius,
|
||||
value.top_radius
|
||||
),
|
||||
SolidHistoryOperation::Pyramid(value) => format!(
|
||||
"{}; height {:.6}; sides {}; radii {:.6}/{:.6}",
|
||||
history_base_text(&value.base),
|
||||
|
||||
@@ -98,7 +98,7 @@ fn plane_distance(a: DVec3, b: DVec3, plane: WorkingPlane) -> f64 {
|
||||
d.x.hypot(d.y)
|
||||
}
|
||||
|
||||
fn tangent_object_local(object: TangentObject, plane: WorkingPlane) -> TangentObject {
|
||||
pub(crate) fn tangent_object_local(object: TangentObject, plane: WorkingPlane) -> TangentObject {
|
||||
match object {
|
||||
TangentObject::Line { p1, p2 } => TangentObject::Line {
|
||||
p1: plane.to_local(p1),
|
||||
@@ -594,7 +594,7 @@ fn solve_quadratic(a: f64, b: f64, c: f64) -> Vec<f64> {
|
||||
vec![(-b - sq) / (2.0 * a), (-b + sq) / (2.0 * a)]
|
||||
}
|
||||
|
||||
fn best_of(candidates: &[DVec3], hint: DVec3) -> Option<DVec3> {
|
||||
pub(crate) fn best_of(candidates: &[DVec3], hint: DVec3) -> Option<DVec3> {
|
||||
candidates.iter().copied().min_by(|a, b| {
|
||||
a.distance(hint)
|
||||
.partial_cmp(&b.distance(hint))
|
||||
@@ -627,7 +627,7 @@ fn tangent_curve(object: TangentObject) -> KernelCurve {
|
||||
}
|
||||
}
|
||||
|
||||
fn ttr_candidates(first: TangentObject, second: TangentObject, radius: f64) -> Vec<DVec3> {
|
||||
pub(crate) fn ttr_candidates(first: TangentObject, second: TangentObject, radius: f64) -> Vec<DVec3> {
|
||||
let first = tangent_curve(first);
|
||||
let second = tangent_curve(second);
|
||||
fillets_between(&first, &second, radius, Tolerance::default())
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -2746,6 +2746,7 @@ impl Scene {
|
||||
self.meshes.remove(&handle);
|
||||
}
|
||||
self.solid_models.insert(handle, solid);
|
||||
self.sync_solid_reference_point(handle);
|
||||
// Only this solid's mesh changed — report just its handle so the mesh /
|
||||
// wire caches patch it in rather than rebuilding the whole drawing (and
|
||||
// so a bulk register loop stays O(n), not O(n²)).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use acadrust::entities::Solid3D;
|
||||
use acadrust::objects::{
|
||||
DynamicBlockData, ObjectType, SolidHistoryBox, SolidHistoryBrep,
|
||||
DynamicBlockData, ObjectType, SolidHistoryBox, SolidHistoryBrep, SolidHistoryCone,
|
||||
SolidHistoryCylinder, SolidHistoryNodeBase, SolidHistoryOperation,
|
||||
SolidHistoryPyramid, SolidHistorySphere, SolidHistoryTorus,
|
||||
};
|
||||
@@ -32,6 +32,13 @@ pub const PROP_WIDTH: &str = "solid_history_width";
|
||||
pub const PROP_HEIGHT: &str = "solid_history_height";
|
||||
pub const PROP_RADIUS: &str = "solid_history_radius";
|
||||
pub const PROP_DIAMETER: &str = "solid_history_diameter";
|
||||
pub const PROP_BASE_RADIUS: &str = "solid_history_base_radius";
|
||||
pub const PROP_TOP_RADIUS: &str = "solid_history_top_radius";
|
||||
pub const PROP_BASE_MAJOR_RADIUS: &str = "solid_history_base_major_radius";
|
||||
pub const PROP_BASE_MINOR_RADIUS: &str = "solid_history_base_minor_radius";
|
||||
pub const PROP_TOP_MAJOR_RADIUS: &str = "solid_history_top_major_radius";
|
||||
pub const PROP_TOP_MINOR_RADIUS: &str = "solid_history_top_minor_radius";
|
||||
pub const PROP_ELLIPTICAL: &str = "solid_history_elliptical";
|
||||
pub const PROP_OUTER_RADIUS: &str = "solid_history_outer_radius";
|
||||
pub const PROP_INNER_RADIUS: &str = "solid_history_inner_radius";
|
||||
pub const PROP_SIDES: &str = "solid_history_sides";
|
||||
@@ -53,7 +60,7 @@ fn history_prop(label: &str, field: &'static str, value: impl ToString) -> Prope
|
||||
fn history_flags(
|
||||
document: &acadrust::CadDocument,
|
||||
handle: acadrust::Handle,
|
||||
) -> Option<(bool, bool)> {
|
||||
) -> Option<(bool, bool, i16)> {
|
||||
let graph = document.solid_history_graph(handle)?;
|
||||
let ObjectType::DynamicBlock(object) = document.objects.get(&graph.root)? else {
|
||||
return None;
|
||||
@@ -61,7 +68,22 @@ fn history_flags(
|
||||
let DynamicBlockData::SolidHistory(history) = &object.data else {
|
||||
return None;
|
||||
};
|
||||
Some((history.record_history, history.show_history))
|
||||
Some((
|
||||
history.record_history,
|
||||
history.show_history,
|
||||
document.header.show_solid_history.clamp(0, 2),
|
||||
))
|
||||
}
|
||||
|
||||
fn displayed_history_state(
|
||||
object_show_history: bool,
|
||||
show_history_mode: i16,
|
||||
) -> (bool, bool) {
|
||||
match show_history_mode {
|
||||
0 => (false, false),
|
||||
2 => (true, false),
|
||||
_ => (object_show_history, true),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_rectangular_primitive(
|
||||
@@ -84,6 +106,7 @@ pub fn has_specialized_primitive_properties(
|
||||
SolidHistoryOperation::Box(_)
|
||||
| SolidHistoryOperation::Wedge(_)
|
||||
| SolidHistoryOperation::Sphere(_)
|
||||
| SolidHistoryOperation::Cone(_)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -97,20 +120,157 @@ pub fn reference_point(operation: &SolidHistoryOperation) -> Option<glam::DVec3>
|
||||
SolidHistoryOperation::Sphere(value) => {
|
||||
world_point(value.base.transform, [0.0, 0.0, 0.0])
|
||||
}
|
||||
SolidHistoryOperation::Cone(value) => world_point(value.base.transform, [0.0; 3]),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn cone_properties(
|
||||
document: &acadrust::CadDocument,
|
||||
handle: acadrust::Handle,
|
||||
value: &SolidHistoryCone,
|
||||
) -> Vec<PropSection> {
|
||||
let Some(position) = world_point(value.base.transform, [0.0; 3]) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let scale = value
|
||||
.base_x_radius
|
||||
.abs()
|
||||
.max(value.base_y_radius.abs())
|
||||
.max(1.0);
|
||||
let elliptical = (value.base_x_radius - value.base_y_radius).abs() > 1e-9 * scale;
|
||||
let rotation = matrix(value.base.transform)
|
||||
.map(|matrix| matrix.x_axis.y.atan2(matrix.x_axis.x))
|
||||
.unwrap_or(0.0);
|
||||
let top_minor_radius = if value.base_x_radius.abs() > 1e-9 {
|
||||
value.top_radius * value.base_y_radius / value.base_x_radius
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let (record_history, object_show_history, show_history_mode) =
|
||||
history_flags(document, handle).unwrap_or((false, false, 1));
|
||||
let (show_history, show_history_editable) =
|
||||
displayed_history_state(object_show_history, show_history_mode);
|
||||
let show_value = if show_history { "Yes" } else { "No" };
|
||||
let mut geometry = vec![
|
||||
Property {
|
||||
label: t!("Solid type").into_owned(),
|
||||
field: "solid_history_type",
|
||||
value: PropValue::ReadOnly(t!("Cone").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,
|
||||
),
|
||||
Property {
|
||||
label: t!("Elliptical").into_owned(),
|
||||
field: PROP_ELLIPTICAL,
|
||||
value: PropValue::ReadOnly(if elliptical { "Yes" } else { "No" }.to_string()),
|
||||
},
|
||||
];
|
||||
if elliptical {
|
||||
geometry.extend([
|
||||
history_prop(
|
||||
t!("Base major radius").as_ref(),
|
||||
PROP_BASE_MAJOR_RADIUS,
|
||||
value.base_x_radius,
|
||||
),
|
||||
history_prop(
|
||||
t!("Base minor radius").as_ref(),
|
||||
PROP_BASE_MINOR_RADIUS,
|
||||
value.base_y_radius,
|
||||
),
|
||||
history_prop(
|
||||
t!("Top major radius").as_ref(),
|
||||
PROP_TOP_MAJOR_RADIUS,
|
||||
value.top_radius,
|
||||
),
|
||||
history_prop(
|
||||
t!("Top minor radius").as_ref(),
|
||||
PROP_TOP_MINOR_RADIUS,
|
||||
top_minor_radius,
|
||||
),
|
||||
Property {
|
||||
label: t!("Rotation").into_owned(),
|
||||
field: PROP_ROTATION,
|
||||
value: PropValue::ReadOnly(crate::entities::common::format_direction(rotation)),
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
geometry.extend([
|
||||
history_prop(
|
||||
t!("Base radius").as_ref(),
|
||||
PROP_BASE_RADIUS,
|
||||
value.base_x_radius,
|
||||
),
|
||||
history_prop(
|
||||
t!("Top radius").as_ref(),
|
||||
PROP_TOP_RADIUS,
|
||||
value.top_radius,
|
||||
),
|
||||
]);
|
||||
}
|
||||
geometry.push(history_prop(
|
||||
t!("Height").as_ref(),
|
||||
PROP_HEIGHT,
|
||||
value.height,
|
||||
));
|
||||
vec![
|
||||
PropSection {
|
||||
title: t!("Geometry").into_owned(),
|
||||
props: geometry,
|
||||
},
|
||||
PropSection {
|
||||
title: t!("Solid History").into_owned(),
|
||||
props: vec![
|
||||
Property {
|
||||
label: t!("History").into_owned(),
|
||||
field: PROP_HISTORY,
|
||||
value: PropValue::Choice {
|
||||
selected: if record_history { "Record" } else { "None" }.to_string(),
|
||||
options: vec!["None".to_string(), "Record".to_string()],
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: t!("Show History").into_owned(),
|
||||
field: PROP_SHOW_HISTORY,
|
||||
value: if show_history_editable {
|
||||
PropValue::Choice {
|
||||
selected: show_value.to_string(),
|
||||
options: vec!["No".to_string(), "Yes".to_string()],
|
||||
}
|
||||
} else {
|
||||
PropValue::ReadOnly(show_value.to_string())
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn sphere_properties(
|
||||
document: &acadrust::CadDocument,
|
||||
handle: acadrust::Handle,
|
||||
value: &SolidHistorySphere,
|
||||
) -> Vec<PropSection> {
|
||||
let Some(position) = world_point(value.base.transform, [0.0, 0.0, 0.0]) else {
|
||||
let Some(position) = world_point(value.base.transform, [0.0; 3]) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let (record_history, show_history) = history_flags(document, handle).unwrap_or((false, false));
|
||||
let history_value = if record_history { "Record" } else { "None" };
|
||||
let (record_history, object_show_history, show_history_mode) =
|
||||
history_flags(document, handle).unwrap_or((false, false, 1));
|
||||
let (show_history, show_history_editable) =
|
||||
displayed_history_state(object_show_history, show_history_mode);
|
||||
let show_value = if show_history { "Yes" } else { "No" };
|
||||
vec![
|
||||
PropSection {
|
||||
@@ -154,12 +314,22 @@ fn sphere_properties(
|
||||
Property {
|
||||
label: t!("History").into_owned(),
|
||||
field: PROP_HISTORY,
|
||||
value: PropValue::ReadOnly(history_value.to_string()),
|
||||
value: PropValue::Choice {
|
||||
selected: if record_history { "Record" } else { "None" }.to_string(),
|
||||
options: vec!["None".to_string(), "Record".to_string()],
|
||||
},
|
||||
},
|
||||
Property {
|
||||
label: t!("Show History").into_owned(),
|
||||
field: PROP_SHOW_HISTORY,
|
||||
value: PropValue::ReadOnly(show_value.to_string()),
|
||||
value: if show_history_editable {
|
||||
PropValue::Choice {
|
||||
selected: show_value.to_string(),
|
||||
options: vec!["No".to_string(), "Yes".to_string()],
|
||||
}
|
||||
} else {
|
||||
PropValue::ReadOnly(show_value.to_string())
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -181,7 +351,10 @@ fn rectangular_properties(
|
||||
let rotation = matrix(value.base.transform)
|
||||
.map(|matrix| matrix.x_axis.y.atan2(matrix.x_axis.x))
|
||||
.unwrap_or(0.0);
|
||||
let (record_history, show_history) = history_flags(document, handle).unwrap_or((false, false));
|
||||
let (record_history, object_show_history, show_history_mode) =
|
||||
history_flags(document, handle).unwrap_or((false, false, 1));
|
||||
let (show_history, show_history_editable) =
|
||||
displayed_history_state(object_show_history, show_history_mode);
|
||||
let show_value = if show_history { "Yes" } else { "No" };
|
||||
vec![
|
||||
PropSection {
|
||||
@@ -245,7 +418,7 @@ fn rectangular_properties(
|
||||
Property {
|
||||
label: t!("Show History").into_owned(),
|
||||
field: PROP_SHOW_HISTORY,
|
||||
value: if record_history {
|
||||
value: if show_history_editable {
|
||||
PropValue::Choice {
|
||||
selected: show_value.to_string(),
|
||||
options: vec!["No".to_string(), "Yes".to_string()],
|
||||
@@ -276,7 +449,8 @@ pub fn primitive_properties(
|
||||
SolidHistoryOperation::Sphere(value) => {
|
||||
return sphere_properties(document, handle, value)
|
||||
}
|
||||
SolidHistoryOperation::Cylinder(value) | SolidHistoryOperation::Cone(value) => vec![
|
||||
SolidHistoryOperation::Cone(value) => return cone_properties(document, handle, value),
|
||||
SolidHistoryOperation::Cylinder(value) => vec![
|
||||
history_prop(t!("Radius").as_ref(), PROP_RADIUS, value.major_radius),
|
||||
history_prop(t!("Height").as_ref(), PROP_HEIGHT, value.height),
|
||||
],
|
||||
@@ -313,6 +487,12 @@ pub fn is_primitive_property(field: &str) -> bool {
|
||||
| PROP_HEIGHT
|
||||
| PROP_RADIUS
|
||||
| PROP_DIAMETER
|
||||
| PROP_BASE_RADIUS
|
||||
| PROP_TOP_RADIUS
|
||||
| PROP_BASE_MAJOR_RADIUS
|
||||
| PROP_BASE_MINOR_RADIUS
|
||||
| PROP_TOP_MAJOR_RADIUS
|
||||
| PROP_TOP_MINOR_RADIUS
|
||||
| PROP_OUTER_RADIUS
|
||||
| PROP_INNER_RADIUS
|
||||
| PROP_SIDES
|
||||
@@ -333,6 +513,7 @@ pub fn apply_history_choice(
|
||||
field: &str,
|
||||
value: &str,
|
||||
) -> bool {
|
||||
let show_history_mode = document.header.show_solid_history.clamp(0, 2);
|
||||
let Some(graph) = document.solid_history_graph(handle) else {
|
||||
return false;
|
||||
};
|
||||
@@ -352,11 +533,8 @@ pub fn apply_history_choice(
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
if !history.record_history {
|
||||
history.show_history = false;
|
||||
}
|
||||
}
|
||||
PROP_SHOW_HISTORY if history.record_history => {
|
||||
PROP_SHOW_HISTORY if show_history_mode == 1 => {
|
||||
history.show_history = if value.eq_ignore_ascii_case("Yes") {
|
||||
true
|
||||
} else if value.eq_ignore_ascii_case("No") {
|
||||
@@ -468,6 +646,36 @@ fn apply_sphere_geometry_property(
|
||||
Some(true)
|
||||
}
|
||||
|
||||
fn apply_cone_position_property(
|
||||
value: &mut SolidHistoryCone,
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn apply_primitive_property(
|
||||
operation: &mut SolidHistoryOperation,
|
||||
field: &str,
|
||||
@@ -487,6 +695,11 @@ pub fn apply_primitive_property(
|
||||
return applied;
|
||||
}
|
||||
}
|
||||
if let SolidHistoryOperation::Cone(cone_value) = operation {
|
||||
if let Some(applied) = apply_cone_position_property(cone_value, field, value) {
|
||||
return applied;
|
||||
}
|
||||
}
|
||||
let Some(number) = (if field == PROP_SIDES {
|
||||
value.trim().parse::<f64>().ok()
|
||||
} else {
|
||||
@@ -533,7 +746,7 @@ pub fn apply_primitive_property(
|
||||
return true;
|
||||
}
|
||||
match operation {
|
||||
SolidHistoryOperation::Cylinder(value) | SolidHistoryOperation::Cone(value) => match field {
|
||||
SolidHistoryOperation::Cylinder(value) => match field {
|
||||
PROP_RADIUS => {
|
||||
let Some(radius) = positive() else {
|
||||
return false;
|
||||
@@ -545,6 +758,46 @@ pub fn apply_primitive_property(
|
||||
PROP_HEIGHT => value.height = positive().unwrap_or(value.height),
|
||||
_ => return false,
|
||||
},
|
||||
SolidHistoryOperation::Cone(value) => match field {
|
||||
PROP_BASE_RADIUS => {
|
||||
let Some(radius) = positive() else {
|
||||
return false;
|
||||
};
|
||||
let ratio = if value.base_x_radius > 1e-9 {
|
||||
value.base_y_radius / value.base_x_radius
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
value.base_x_radius = radius;
|
||||
value.base_y_radius = radius * ratio;
|
||||
}
|
||||
PROP_BASE_MAJOR_RADIUS => {
|
||||
value.base_x_radius = positive().unwrap_or(value.base_x_radius);
|
||||
}
|
||||
PROP_BASE_MINOR_RADIUS => {
|
||||
value.base_y_radius = positive().unwrap_or(value.base_y_radius);
|
||||
}
|
||||
PROP_TOP_RADIUS => {
|
||||
if number < 0.0 {
|
||||
return false;
|
||||
}
|
||||
value.top_radius = number;
|
||||
}
|
||||
PROP_TOP_MAJOR_RADIUS => {
|
||||
if number < 0.0 {
|
||||
return false;
|
||||
}
|
||||
value.top_radius = number;
|
||||
}
|
||||
PROP_TOP_MINOR_RADIUS => {
|
||||
if number < 0.0 || value.base_y_radius.abs() <= 1e-9 {
|
||||
return false;
|
||||
}
|
||||
value.top_radius = number * value.base_x_radius / value.base_y_radius;
|
||||
}
|
||||
PROP_HEIGHT => value.height = positive().unwrap_or(value.height),
|
||||
_ => return false,
|
||||
},
|
||||
SolidHistoryOperation::Sphere(value) => {
|
||||
let Some(next) = positive() else {
|
||||
return false;
|
||||
@@ -604,7 +857,12 @@ pub fn apply_primitive_property(
|
||||
},
|
||||
_ => return false,
|
||||
}
|
||||
positive().is_some() || field == PROP_SIDES
|
||||
positive().is_some()
|
||||
|| field == PROP_SIDES
|
||||
|| matches!(
|
||||
field,
|
||||
PROP_TOP_RADIUS | PROP_TOP_MAJOR_RADIUS | PROP_TOP_MINOR_RADIUS
|
||||
)
|
||||
}
|
||||
|
||||
fn matrix(transform: [f64; 16]) -> Option<glam::DMat4> {
|
||||
@@ -803,7 +1061,7 @@ pub fn primitive_grips(
|
||||
Some([0.0, 0.0, 1.0]),
|
||||
);
|
||||
}
|
||||
SolidHistoryOperation::Cylinder(value) | SolidHistoryOperation::Cone(value) => {
|
||||
SolidHistoryOperation::Cylinder(value) => {
|
||||
add(
|
||||
GRIP_RADIUS,
|
||||
value.base.transform,
|
||||
@@ -819,6 +1077,22 @@ pub fn primitive_grips(
|
||||
Some([0.0, 0.0, 1.0]),
|
||||
);
|
||||
}
|
||||
SolidHistoryOperation::Cone(value) => {
|
||||
add(
|
||||
GRIP_RADIUS,
|
||||
value.base.transform,
|
||||
[value.base_x_radius, 0.0, 0.0],
|
||||
GripShape::Square,
|
||||
None,
|
||||
);
|
||||
add(
|
||||
GRIP_HEIGHT,
|
||||
value.base.transform,
|
||||
[0.0, 0.0, value.height],
|
||||
GripShape::Square,
|
||||
Some([0.0, 0.0, 1.0]),
|
||||
);
|
||||
}
|
||||
SolidHistoryOperation::Sphere(value) => add(
|
||||
GRIP_RADIUS,
|
||||
value.base.transform,
|
||||
@@ -952,7 +1226,7 @@ pub fn apply_primitive_grip(
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
SolidHistoryOperation::Cylinder(value) | SolidHistoryOperation::Cone(value) => {
|
||||
SolidHistoryOperation::Cylinder(value) => {
|
||||
match grip_id {
|
||||
GRIP_RADIUS => {
|
||||
let radius = local.x.hypot(local.y).max(1e-6);
|
||||
@@ -964,6 +1238,20 @@ pub fn apply_primitive_grip(
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
SolidHistoryOperation::Cone(value) => match grip_id {
|
||||
GRIP_RADIUS => {
|
||||
let radius = local.x.hypot(local.y).max(1e-6);
|
||||
let ratio = if value.base_x_radius > 1e-9 {
|
||||
value.base_y_radius / value.base_x_radius
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
value.base_x_radius = radius;
|
||||
value.base_y_radius = radius * ratio;
|
||||
}
|
||||
GRIP_HEIGHT => value.height = local.z.max(1e-6),
|
||||
_ => return false,
|
||||
},
|
||||
SolidHistoryOperation::Sphere(value) if grip_id == GRIP_RADIUS => {
|
||||
value.radius = local.length().max(1e-6);
|
||||
}
|
||||
@@ -1059,17 +1347,19 @@ pub fn cylinder_op(
|
||||
|
||||
pub fn cone_op(
|
||||
transform: [f64; 16],
|
||||
radius: f64,
|
||||
base_x_radius: f64,
|
||||
base_y_radius: f64,
|
||||
top_radius: f64,
|
||||
height: f64,
|
||||
) -> SolidHistoryOperation {
|
||||
SolidHistoryOperation::Cone(SolidHistoryCylinder {
|
||||
SolidHistoryOperation::Cone(SolidHistoryCone {
|
||||
base: base(transform),
|
||||
operation_major: 1,
|
||||
height,
|
||||
major_radius: radius,
|
||||
minor_radius: radius,
|
||||
x_radius: radius,
|
||||
..SolidHistoryCylinder::default()
|
||||
base_x_radius,
|
||||
base_y_radius,
|
||||
top_radius,
|
||||
..SolidHistoryCone::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -40,9 +40,21 @@ pub fn cylinder_solid(center: [f64; 3], radius: f64, height: f64) -> Option<Body
|
||||
brep::make::cylinder(center, radius, height)
|
||||
}
|
||||
|
||||
/// Solid cone standing on the z = base plane, apex `height` above it.
|
||||
pub fn cone_solid(center: [f64; 3], radius: f64, height: f64) -> Option<Body> {
|
||||
brep::make::cone(center, radius, height)
|
||||
/// Circular or elliptical cone/frustum standing on the z = base plane.
|
||||
pub fn cone_frustum_solid(
|
||||
center: [f64; 3],
|
||||
base_x_radius: f64,
|
||||
base_y_radius: f64,
|
||||
top_radius: f64,
|
||||
height: f64,
|
||||
) -> Option<Body> {
|
||||
brep::make::frustum(
|
||||
center,
|
||||
base_x_radius,
|
||||
base_y_radius,
|
||||
top_radius,
|
||||
height,
|
||||
)
|
||||
}
|
||||
|
||||
/// Solid sphere about `center`.
|
||||
@@ -452,7 +464,7 @@ mod tests {
|
||||
assert!(tri_count(&box_solid(c, 10.0, 10.0, 10.0).unwrap()) >= 12, "box");
|
||||
assert!(tri_count(&wedge_solid(c, 10.0, 10.0, 10.0).unwrap()) >= 6, "wedge");
|
||||
assert!(tri_count(&cylinder_solid(c, 5.0, 12.0).unwrap()) > 20, "cylinder");
|
||||
assert!(tri_count(&cone_solid(c, 5.0, 12.0).unwrap()) > 10, "cone");
|
||||
assert!(tri_count(&cone_frustum_solid(c, 5.0, 5.0, 0.0, 12.0).unwrap()) > 10, "cone");
|
||||
assert!(tri_count(&sphere_solid(c, 5.0).unwrap()) > 50, "sphere");
|
||||
assert!(tri_count(&torus_solid(c, 8.0, 2.0).unwrap()) > 50, "torus");
|
||||
assert!(tri_count(&pyramid_solid(c, 5.0, 9.0, 6).unwrap()) >= 8, "pyramid");
|
||||
@@ -469,7 +481,10 @@ mod tests {
|
||||
let cases: [(Body, f64); 5] = [
|
||||
(box_solid(c, 10.0, 4.0, 6.0).unwrap(), 240.0),
|
||||
(cylinder_solid(c, 5.0, 12.0).unwrap(), PI * 25.0 * 12.0),
|
||||
(cone_solid(c, 5.0, 12.0).unwrap(), PI * 25.0 * 12.0 / 3.0),
|
||||
(
|
||||
cone_frustum_solid(c, 5.0, 5.0, 0.0, 12.0).unwrap(),
|
||||
PI * 25.0 * 12.0 / 3.0,
|
||||
),
|
||||
(sphere_solid(c, 5.0).unwrap(), 4.0 / 3.0 * PI * 125.0),
|
||||
(torus_solid(c, 8.0, 2.0).unwrap(), 2.0 * PI * PI * 8.0 * 4.0),
|
||||
];
|
||||
|
||||
+1
-1
@@ -802,7 +802,7 @@ impl Scene {
|
||||
true
|
||||
}
|
||||
|
||||
fn sync_solid_reference_point(&mut self, handle: Handle) {
|
||||
pub(crate) fn sync_solid_reference_point(&mut self, handle: Handle) {
|
||||
let reference = self
|
||||
.document
|
||||
.solid_history_operation(handle)
|
||||
|
||||
Reference in new issue
Block a user