KCL: involuteCircular can use diameter in addition to radius (#7519)

Mechanical engineers prefer using diameter over radius.
This commit is contained in:
Adam Chalmers
2025-06-19 09:09:24 -05:00
committed by GitHub
parent de6e0f6b18
commit 9eaacc2a51
7 changed files with 118 additions and 145 deletions

View File

@ -10,9 +10,11 @@ Extend the current sketch with a new involute circular curve.
```kcl
involuteCircular(
@sketch: Sketch,
startRadius: number(Length),
endRadius: number(Length),
angle: number(Angle),
startRadius?: number(Length),
endRadius?: number(Length),
startDiameter?: number(Length),
endDiameter?: number(Length),
reverse?: bool,
tag?: TagDecl,
): Sketch
@ -25,9 +27,11 @@ involuteCircular(
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `sketch` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) | Which sketch should this path be added to? | Yes |
| `startRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, start_radius is the radius of the inner circle. | Yes |
| `endRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, end_radius is the radius of the outer circle. | Yes |
| `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | The angle to rotate the involute by. A value of zero will produce a curve with a tangent along the x-axis at the start point of the curve. | Yes |
| `startRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, startRadius is the radius of the inner circle. Either `startRadius` or `startDiameter` must be given (but not both). | No |
| `endRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, endRadius is the radius of the outer circle. Either `endRadius` or `endDiameter` must be given (but not both). | No |
| `startDiameter` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, startDiameter describes the inner circle. Either `startRadius` or `startDiameter` must be given (but not both). | No |
| `endDiameter` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, endDiameter describes the outer circle. Either `endRadius` or `endDiameter` must be given (but not both). | No |
| `reverse` | [`bool`](/docs/kcl-std/types/std-types-bool) | If reverse is true, the segment will start from the end of the involute, otherwise it will start from that start. | No |
| `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this line. | No |

View File

@ -42,15 +42,15 @@ fn helicalGear(nTeeth, module, pressureAngle, helixAngle, gearHeight) {
helicalGearSketch = startSketchOn(offsetPlane(XY, offset = offsetHeight))
|> startProfile(at = polar(angle = helixCalc, length = baseDiameter / 2))
|> involuteCircular(
startRadius = baseDiameter / 2,
endRadius = tipDiameter / 2,
startDiameter = baseDiameter,
endDiameter = tipDiameter,
angle = helixCalc,
tag = $seg01,
)
|> line(endAbsolute = polar(angle = 160 / nTeeth + helixCalc, length = tipDiameter / 2))
|> involuteCircular(
startRadius = baseDiameter / 2,
endRadius = tipDiameter / 2,
startDiameter = baseDiameter,
endDiameter = tipDiameter,
angle = -(4 * atan(segEndY(seg01) / segEndX(seg01)) - (3 * helixCalc)),
reverse = true,
)

View File

@ -415,16 +415,26 @@ pub(crate) fn get_radius(
radius: Option<TyF64>,
diameter: Option<TyF64>,
source_range: SourceRange,
) -> Result<TyF64, KclError> {
get_radius_labelled(radius, diameter, source_range, "radius", "diameter")
}
pub(crate) fn get_radius_labelled(
radius: Option<TyF64>,
diameter: Option<TyF64>,
source_range: SourceRange,
label_radius: &'static str,
label_diameter: &'static str,
) -> Result<TyF64, KclError> {
match (radius, diameter) {
(Some(radius), None) => Ok(radius),
(None, Some(diameter)) => Ok(TyF64::new(diameter.n / 2.0, diameter.ty)),
(None, None) => Err(KclError::new_type(KclErrorDetails::new(
"This function needs either `diameter` or `radius`".to_string(),
format!("This function needs either `{label_diameter}` or `{label_radius}`"),
vec![source_range],
))),
(Some(_), Some(_)) => Err(KclError::new_type(KclErrorDetails::new(
"You cannot specify both `diameter` and `radius`, please remove one".to_string(),
format!("You cannot specify both `{label_diameter}` and `{label_radius}`, please remove one"),
vec![source_range],
))),
}

View File

@ -11,7 +11,7 @@ use parse_display::{Display, FromStr};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use super::shapes::get_radius;
use super::shapes::{get_radius, get_radius_labelled};
#[cfg(feature = "artifact-graph")]
use crate::execution::{Artifact, ArtifactId, CodeRef, StartSketchOnFace, StartSketchOnPlane};
use crate::{
@ -101,13 +101,26 @@ pub const NEW_TAG_KW: &str = "tag";
pub async fn involute_circular(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketch = args.get_unlabeled_kw_arg("sketch", &RuntimeType::sketch(), exec_state)?;
let start_radius: TyF64 = args.get_kw_arg("startRadius", &RuntimeType::length(), exec_state)?;
let end_radius: TyF64 = args.get_kw_arg("endRadius", &RuntimeType::length(), exec_state)?;
let start_radius: Option<TyF64> = args.get_kw_arg_opt("startRadius", &RuntimeType::length(), exec_state)?;
let end_radius: Option<TyF64> = args.get_kw_arg_opt("endRadius", &RuntimeType::length(), exec_state)?;
let start_diameter: Option<TyF64> = args.get_kw_arg_opt("startDiameter", &RuntimeType::length(), exec_state)?;
let end_diameter: Option<TyF64> = args.get_kw_arg_opt("endDiameter", &RuntimeType::length(), exec_state)?;
let angle: TyF64 = args.get_kw_arg("angle", &RuntimeType::angle(), exec_state)?;
let reverse = args.get_kw_arg_opt("reverse", &RuntimeType::bool(), exec_state)?;
let tag = args.get_kw_arg_opt("tag", &RuntimeType::tag_decl(), exec_state)?;
let new_sketch =
inner_involute_circular(sketch, start_radius, end_radius, angle, reverse, tag, exec_state, args).await?;
let new_sketch = inner_involute_circular(
sketch,
start_radius,
end_radius,
start_diameter,
end_diameter,
angle,
reverse,
tag,
exec_state,
args,
)
.await?;
Ok(KclValue::Sketch {
value: Box::new(new_sketch),
})
@ -123,8 +136,10 @@ fn involute_curve(radius: f64, angle: f64) -> (f64, f64) {
#[allow(clippy::too_many_arguments)]
async fn inner_involute_circular(
sketch: Sketch,
start_radius: TyF64,
end_radius: TyF64,
start_radius: Option<TyF64>,
end_radius: Option<TyF64>,
start_diameter: Option<TyF64>,
end_diameter: Option<TyF64>,
angle: TyF64,
reverse: Option<bool>,
tag: Option<TagNode>,
@ -133,6 +148,22 @@ async fn inner_involute_circular(
) -> Result<Sketch, KclError> {
let id = exec_state.next_uuid();
let longer_args_dot_source_range = args.source_range;
let start_radius = get_radius_labelled(
start_radius,
start_diameter,
args.source_range,
"startRadius",
"startDiameter",
)?;
let end_radius = get_radius_labelled(
end_radius,
end_diameter,
longer_args_dot_source_range,
"endRadius",
"endDiameter",
)?;
exec_state
.batch_modeling_cmd(
ModelingCmdMeta::from_args_id(&args, id),

View File

@ -1499,12 +1499,20 @@ export fn profileStartY(
export fn involuteCircular(
/// Which sketch should this path be added to?
@sketch: Sketch,
/// The involute is described between two circles, start_radius is the radius of the inner circle.
startRadius: number(Length),
/// The involute is described between two circles, end_radius is the radius of the outer circle.
endRadius: number(Length),
/// The angle to rotate the involute by. A value of zero will produce a curve with a tangent along the x-axis at the start point of the curve.
angle: number(Angle),
/// The involute is described between two circles, startRadius is the radius of the inner circle.
/// Either `startRadius` or `startDiameter` must be given (but not both).
startRadius?: number(Length),
/// The involute is described between two circles, endRadius is the radius of the outer circle.
/// Either `endRadius` or `endDiameter` must be given (but not both).
endRadius?: number(Length),
/// The involute is described between two circles, startDiameter describes the inner circle.
/// Either `startRadius` or `startDiameter` must be given (but not both).
startDiameter?: number(Length),
/// The involute is described between two circles, endDiameter describes the outer circle.
/// Either `endRadius` or `endDiameter` must be given (but not both).
endDiameter?: number(Length),
/// If reverse is true, the segment will start from the end of the involute, otherwise it will start from that start.
reverse?: bool = false,
/// Create a new tag which refers to this line.

View File

@ -20,37 +20,37 @@ flowchart LR
subgraph path11 [Path]
11["Path<br>[1779, 1849, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }]
12["Segment<br>[1859, 2025, 0]"]
12["Segment<br>[1859, 2021, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 2 }]
13["Segment<br>[2035, 2120, 0]"]
13["Segment<br>[2031, 2116, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 3 }]
14["Segment<br>[2130, 2351, 0]"]
14["Segment<br>[2126, 2343, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 4 }]
15["Segment<br>[2438, 2524, 0]"]
15["Segment<br>[2430, 2516, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 5 }]
16["Segment<br>[2813, 2820, 0]"]
16["Segment<br>[2805, 2812, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 7 }]
17[Solid2d]
end
subgraph path19 [Path]
19["Path<br>[1779, 1849, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }]
20["Segment<br>[1859, 2025, 0]"]
20["Segment<br>[1859, 2021, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 2 }]
21["Segment<br>[2035, 2120, 0]"]
21["Segment<br>[2031, 2116, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 3 }]
22["Segment<br>[2130, 2351, 0]"]
22["Segment<br>[2126, 2343, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 4 }]
23["Segment<br>[2438, 2524, 0]"]
23["Segment<br>[2430, 2516, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 5 }]
24["Segment<br>[2813, 2820, 0]"]
24["Segment<br>[2805, 2812, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 7 }]
25[Solid2d]
end
subgraph path27 [Path]
27["Path<br>[1779, 1849, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }]
32["Segment<br>[2813, 2820, 0]"]
32["Segment<br>[2805, 2812, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 7 }]
33[Solid2d]
end
@ -66,7 +66,7 @@ flowchart LR
29["SweepEdge Opposite"]
30["SweepEdge Opposite"]
31["SweepEdge Opposite"]
34["Sweep Loft<br>[3337, 3404, 0]"]
34["Sweep Loft<br>[3329, 3396, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 15 }, VariableDeclarationDeclaration, VariableDeclarationInit]
35[Wall]
%% face_code_ref=Missing NodePath

View File

@ -1735,14 +1735,11 @@ description: Result of parsing helical-gear.kcl
"label": {
"commentStart": 0,
"end": 0,
"name": "startRadius",
"name": "startDiameter",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"left": {
"abs_path": false,
"commentStart": 0,
"end": 0,
@ -1757,23 +1754,6 @@ description: Result of parsing helical-gear.kcl
"start": 0,
"type": "Name",
"type": "Name"
},
"operator": "/",
"right": {
"commentStart": 0,
"end": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
},
"start": 0,
"type": "BinaryExpression",
"type": "BinaryExpression"
}
},
{
@ -1781,14 +1761,11 @@ description: Result of parsing helical-gear.kcl
"label": {
"commentStart": 0,
"end": 0,
"name": "endRadius",
"name": "endDiameter",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"left": {
"abs_path": false,
"commentStart": 0,
"end": 0,
@ -1803,23 +1780,6 @@ description: Result of parsing helical-gear.kcl
"start": 0,
"type": "Name",
"type": "Name"
},
"operator": "/",
"right": {
"commentStart": 0,
"end": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
},
"start": 0,
"type": "BinaryExpression",
"type": "BinaryExpression"
}
},
{
@ -2072,14 +2032,11 @@ description: Result of parsing helical-gear.kcl
"label": {
"commentStart": 0,
"end": 0,
"name": "startRadius",
"name": "startDiameter",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"left": {
"abs_path": false,
"commentStart": 0,
"end": 0,
@ -2094,23 +2051,6 @@ description: Result of parsing helical-gear.kcl
"start": 0,
"type": "Name",
"type": "Name"
},
"operator": "/",
"right": {
"commentStart": 0,
"end": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
},
"start": 0,
"type": "BinaryExpression",
"type": "BinaryExpression"
}
},
{
@ -2118,14 +2058,11 @@ description: Result of parsing helical-gear.kcl
"label": {
"commentStart": 0,
"end": 0,
"name": "endRadius",
"name": "endDiameter",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"left": {
"abs_path": false,
"commentStart": 0,
"end": 0,
@ -2140,23 +2077,6 @@ description: Result of parsing helical-gear.kcl
"start": 0,
"type": "Name",
"type": "Name"
},
"operator": "/",
"right": {
"commentStart": 0,
"end": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
},
"start": 0,
"type": "BinaryExpression",
"type": "BinaryExpression"
}
},
{