feat(model): complete cone workflow

This commit is contained in:
Hakan Seven committed 2026-08-31 22:07:49 +03:00
commit 72669f58f3
11 files changed
+1217 -66

No files matched your search

Generated
+2 -2
View File
@@ -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
View File
@@ -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"] }
+2 -2
View File
@@ -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"
+1 -1
View File
@@ -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"
+11 -2
View File
@@ -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),
+3 -3
View File
@@ -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
+1
View File
@@ -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²)).
+315 -25
View File
@@ -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()
})
}
+20 -5
View File
@@ -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
View File
@@ -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)