use crate::feature_pipeline::features::{common, transform_bake};
use crate::feature_pipeline::{FeatureContext, FeatureResult};
use crate::{make_cone_brep, Vec3};
pub fn execute(ctx: &FeatureContext) -> FeatureResult {
match build(ctx) {
Ok(result) => result,
Err(error) => ctx.fail(error),
}
}
const APEX_RADIUS: f64 = 1e-7;
fn build(ctx: &FeatureContext) -> Result<FeatureResult, String> {
let radius_top = ctx.number("radiusTop")?.abs().max(0.0);
let radius_bottom = ctx.number("radiusBottom")?.abs();
let height = ctx.number("height")?;
if height.abs() <= 0.0 {
return Err("cone: height must be non-zero".into());
}
if radius_bottom <= APEX_RADIUS && radius_top <= APEX_RADIUS {
return Err("cone: at least one of radiusTop / radiusBottom must be positive".into());
}
let inverted = radius_bottom <= APEX_RADIUS;
let mut solid = if inverted {
make_cone_brep(
Vec3::new(0.0, height.abs(), 0.0),
Vec3::new(0.0, -1.0, 0.0),
radius_top,
0.0,
height.abs(),
)?
} else {
make_cone_brep(
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
radius_bottom,
radius_top,
height.abs(),
)?
};
let faces = &mut solid
.shells
.get_mut(0)
.ok_or("cone builder produced no shell")?
.faces;
let suffixes: &[&str] = match (faces.len(), inverted) {
(2, false) => &["_S", "_B"],
(2, true) => &["_S", "_T"],
(3, false) => &["_S", "_B", "_T"],
(3, true) => return Err("cone builder produced a frustum for a zero bottom radius".into()),
(other, _) => return Err(format!("cone builder produced {other} faces, expected 2 or 3")),
};
for (face, suffix) in faces.iter_mut().zip(suffixes) {
face.name = Some(format!("{}{}", ctx.id, suffix));
}
let solid = if height < 0.0 {
common::rotate_half_turn_about_x(solid)?
} else {
solid
};
let solid = transform_bake::apply(ctx, solid)?;
Ok(common::finalize_solid(ctx, solid, &ctx.id))
}
pub fn context_applicable(_probe: &crate::feature_pipeline::SelectionProbe) -> bool {
false
}
pub fn schema() -> serde_json::Value {
serde_json::json!({
"type": "P.CO",
"shortName": "P.CO",
"longName": "Primitive Cone",
"ribbonPath": "Home/Primitives/Cone",
"commandSize": "Compact",
"displayBuilder": false,
"inputParamsSchema": {
"id": {
"type": "string",
"default_value": null,
"hint": "Unique identifier for the feature"
},
"radiusTop": {
"type": "number",
"default_value": 5,
"hint": "Top radius of the cone (tip if 0)"
},
"radiusBottom": {
"type": "number",
"default_value": 10,
"hint": "Base radius of the cone (tip if 0)"
},
"height": {
"type": "number",
"default_value": 10,
"hint": "Height of the cone along Y-axis"
},
"transform": common::primitive_transform_schema(),
"boolean": common::optional_boolean_schema()
}
})
}