Move more functions to KCL decls (#7266)

* Move some sketch functions to KCL

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Move asserts to KCL

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* sweep, loft -> KCL

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Move pattern transforms to KCL

Signed-off-by: Nick Cameron <nrc@ncameron.org>

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-05-30 11:00:16 +12:00
committed by GitHub
parent 46b6707e3a
commit 80e3dc9095
52 changed files with 974 additions and 78951 deletions

View File

@ -644,13 +644,13 @@ impl FnData {
format!("{}({})", self.preferred_name, args.join(", "))
}
fn to_signature_help(&self) -> SignatureHelp {
pub(crate) fn to_signature_help(&self) -> SignatureHelp {
// TODO Fill this in based on the current position of the cursor.
let active_parameter = None;
SignatureHelp {
signatures: vec![SignatureInformation {
label: self.preferred_name.clone(),
label: self.preferred_name.clone() + &self.fn_signature(),
documentation: self.short_docs().map(|s| {
Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
@ -824,6 +824,7 @@ impl ArgData {
),
)),
Some("Axis2d | Edge") | Some("Axis3d | Edge") => Some((index, format!(r#"{label}${{{index}:X}}"#))),
Some("Sketch") | Some("Sketch | Helix") => Some((index, format!(r#"{label}${{{index}:sketch000}}"#))),
Some("Edge") => Some((index, format!(r#"{label}${{{index}:tag_or_edge_fn}}"#))),
Some("[Edge; 1+]") => Some((index, format!(r#"{label}[${{{index}:tag_or_edge_fn}}]"#))),
Some("Plane") => Some((index, format!(r#"{label}${{{}:XY}}"#, index))),
@ -1280,7 +1281,10 @@ mod test {
continue;
};
for i in 0..f.examples.len() {
for (i, (_, props)) in f.examples.iter().enumerate() {
if props.norun {
continue;
}
let name = format!("{}-{i}", f.qual_name.replace("::", "-"));
assert!(TEST_NAMES.contains(&&*name), "Missing test for example \"{name}\", maybe need to update kcl-derive-docs/src/example_tests.rs?")
}

View File

@ -976,9 +976,12 @@ mod tests {
#[test]
fn get_autocomplete_snippet_extrude() {
let extrude_fn: Box<dyn StdLibFn> = Box::new(crate::std::extrude::Extrude);
let snippet = extrude_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"extrude(${0:%}, length = ${1:3.14})"#);
let data = kcl_doc::walk_prelude();
let DocData::Fn(data) = data.find_by_name("extrude").unwrap() else {
panic!();
};
let snippet = data.to_autocomplete_snippet();
assert_eq!(snippet, r#"extrude(length = ${0:10})"#);
}
#[test]
@ -1064,11 +1067,14 @@ mod tests {
#[test]
fn get_autocomplete_snippet_pattern_linear_2d() {
let pattern_fn: Box<dyn StdLibFn> = Box::new(crate::std::patterns::PatternLinear2D);
let snippet = pattern_fn.to_autocomplete_snippet().unwrap();
let data = kcl_doc::walk_prelude();
let DocData::Fn(data) = data.find_by_name("patternLinear2d").unwrap() else {
panic!();
};
let snippet = data.to_autocomplete_snippet();
assert_eq!(
snippet,
r#"patternLinear2d(${0:%}, instances = ${1:10}, distance = ${2:3.14}, axis = [${3:1}, ${4:0}])"#
r#"patternLinear2d(instances = ${0:10}, distance = ${1:10}, axis = [${2:1}, ${3:0}])"#
);
}
@ -1084,16 +1090,22 @@ mod tests {
#[test]
fn get_autocomplete_snippet_loft() {
let loft_fn: Box<dyn StdLibFn> = Box::new(crate::std::loft::Loft);
let snippet = loft_fn.to_autocomplete_snippet().unwrap();
let data = kcl_doc::walk_prelude();
let DocData::Fn(data) = data.find_by_name("loft").unwrap() else {
panic!();
};
let snippet = data.to_autocomplete_snippet();
assert_eq!(snippet, r#"loft([${0:sketch000}, ${1:sketch001}])"#);
}
#[test]
fn get_autocomplete_snippet_sweep() {
let sweep_fn: Box<dyn StdLibFn> = Box::new(crate::std::sweep::Sweep);
let snippet = sweep_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"sweep(${0:%}, path = ${1:sketch000})"#);
let data = kcl_doc::walk_prelude();
let DocData::Fn(data) = data.find_by_name("sweep").unwrap() else {
panic!();
};
let snippet = data.to_autocomplete_snippet();
assert_eq!(snippet, r#"sweep(path = ${0:sketch000})"#);
}
#[test]
@ -1225,18 +1237,21 @@ mod tests {
#[test]
fn get_extrude_signature_help() {
let extrude_fn: Box<dyn StdLibFn> = Box::new(crate::std::extrude::Extrude);
let sh = extrude_fn.to_signature_help();
let data = kcl_doc::walk_prelude();
let DocData::Fn(data) = data.find_by_name("extrude").unwrap() else {
panic!();
};
let sh = data.to_signature_help();
assert_eq!(
sh.signatures[0].label,
r#"extrude(
@sketches: [Sketch],
length: number,
@sketches: [Sketch; 1+],
length: number(Length),
symmetric?: bool,
bidirectionalLength?: number,
tagStart?: TagNode,
tagEnd?: TagNode,
): [Solid]"#
bidirectionalLength?: number(Length),
tagStart?: tag,
tagEnd?: tag,
): [Solid; 1+]"#
);
}
}

View File

@ -1202,17 +1202,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
"Expected one signature, got {:?}",
signature_help.signatures
);
assert_eq!(
signature_help.signatures[0].label,
r#"extrude(
@sketches: [Sketch],
length: number,
symmetric?: bool,
bidirectionalLength?: number,
tagStart?: TagNode,
tagEnd?: TagNode,
): [Solid]"#
);
assert!(signature_help.signatures[0].label.starts_with("extrude"));
} else {
panic!("Expected signature help");
}
@ -1300,17 +1290,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
"Expected one signature, got {:?}",
signature_help.signatures
);
assert_eq!(
signature_help.signatures[0].label,
r#"extrude(
@sketches: [Sketch],
length: number,
symmetric?: bool,
bidirectionalLength?: number,
tagStart?: TagNode,
tagEnd?: TagNode,
): [Solid]"#
);
assert!(signature_help.signatures[0].label.starts_with("extrude"));
} else {
panic!("Expected signature help");
}
@ -1393,17 +1373,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
"Expected one signature, got {:?}",
signature_help.signatures
);
assert_eq!(
signature_help.signatures[0].label,
r#"extrude(
@sketches: [Sketch],
length: number,
symmetric?: bool,
bidirectionalLength?: number,
tagStart?: TagNode,
tagEnd?: TagNode,
): [Solid]"#
);
assert!(signature_help.signatures[0].label.starts_with("extrude"));
} else {
panic!("Expected signature help");
}
@ -1491,17 +1461,7 @@ a1 = startSketchOn(offsetPlane(XY, offset = 10))
"Expected one signature, got {:?}",
signature_help.signatures
);
assert_eq!(
signature_help.signatures[0].label,
r#"extrude(
@sketches: [Sketch],
length: number,
symmetric?: bool,
bidirectionalLength?: number,
tagStart?: TagNode,
tagEnd?: TagNode,
): [Solid]"#
);
assert!(signature_help.signatures[0].label.starts_with("extrude"));
} else {
panic!("Expected signature help");
}

View File

@ -1,7 +1,6 @@
//! Standard library assert functions.
use anyhow::Result;
use kcl_derive_docs::stdlib;
use super::args::TyF64;
use crate::{
@ -42,19 +41,6 @@ pub async fn assert(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
Ok(KclValue::none())
}
/// Asserts that a value is the boolean value true.
/// ```no_run
/// kclIsFun = true
/// assertIs(kclIsFun)
/// ```
#[stdlib{
name = "assertIs",
unlabeled_first = true,
args = {
actual = { docs = "Value to check. If this is the boolean value true, assert passes. Otherwise it fails." },
error = { docs = "If the value was false, the program will terminate with this error message" },
}
}]
async fn inner_assert_is(actual: bool, error: Option<String>, args: &Args) -> Result<(), KclError> {
let error_msg = match &error {
Some(x) => x,
@ -63,29 +49,6 @@ async fn inner_assert_is(actual: bool, error: Option<String>, args: &Args) -> Re
_assert(actual, error_msg, args).await
}
/// Check a value meets some expected conditions at runtime. Program terminates with an error if conditions aren't met.
/// If you provide multiple conditions, they will all be checked and all must be met.
///
/// ```no_run
/// n = 10
/// assert(n, isEqualTo = 10)
/// assert(n, isGreaterThanOrEqual = 0, isLessThan = 100, error = "number should be between 0 and 100")
/// assert(1.0000000000012, isEqualTo = 1, tolerance = 0.0001, error = "number should be almost exactly 1")
/// ```
#[stdlib {
name = "assert",
unlabeled_first = true,
args = {
actual = { docs = "Value to check. It will be compared with one of the comparison arguments." },
is_greater_than = { docs = "Comparison argument. If given, checks the `actual` value is greater than this." },
is_less_than = { docs = "Comparison argument. If given, checks the `actual` value is less than this." },
is_greater_than_or_equal = { docs = "Comparison argument. If given, checks the `actual` value is greater than or equal to this." },
is_less_than_or_equal = { docs = "Comparison argument. If given, checks the `actual` value is less than or equal to this." },
is_equal_to = { docs = "Comparison argument. If given, checks the `actual` value is less than or equal to this.", include_in_snippet = true },
tolerance = { docs = "If `isEqualTo` is used, this is the tolerance to allow for the comparison. This tolerance is used because KCL's number system has some floating-point imprecision when used with very large decimal places." },
error = { docs = "If the value was false, the program will terminate with this error message" },
}
}]
#[allow(clippy::too_many_arguments)]
async fn inner_assert(
actual: TyF64,

View File

@ -3,7 +3,6 @@
use std::collections::HashMap;
use anyhow::Result;
use kcl_derive_docs::stdlib;
use kcmc::{
each_cmd as mcmd,
length_unit::LengthUnit,
@ -52,113 +51,6 @@ pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
Ok(result.into())
}
/// Extend a 2-dimensional sketch through a third dimension in order to
/// create new 3-dimensional volume, or if extruded into an existing volume,
/// cut into an existing solid.
///
/// You can provide more than one sketch to extrude, and they will all be
/// extruded in the same direction.
///
/// ```no_run
/// example = startSketchOn(XZ)
/// |> startProfile(at = [0, 0])
/// |> line(end = [10, 0])
/// |> arc(
/// angleStart = 120,
/// angleEnd = 0,
/// radius = 5,
/// )
/// |> line(end = [5, 0])
/// |> line(end = [0, 10])
/// |> bezierCurve(
/// control1 = [-10, 0],
/// control2 = [2, 10],
/// end = [-5, 10],
/// )
/// |> line(end = [-5, -2])
/// |> close()
/// |> extrude(length = 10)
/// ```
///
/// ```no_run
/// exampleSketch = startSketchOn(XZ)
/// |> startProfile(at = [-10, 0])
/// |> arc(
/// angleStart = 120,
/// angleEnd = -60,
/// radius = 5,
/// )
/// |> line(end = [10, 0])
/// |> line(end = [5, 0])
/// |> bezierCurve(
/// control1 = [-3, 0],
/// control2 = [2, 10],
/// end = [-5, 10],
/// )
/// |> line(end = [-4, 10])
/// |> line(end = [-5, -2])
/// |> close()
///
/// example = extrude(exampleSketch, length = 10)
/// ```
///
/// ```no_run
/// exampleSketch = startSketchOn(XZ)
/// |> startProfile(at = [-10, 0])
/// |> arc(
/// angleStart = 120,
/// angleEnd = -60,
/// radius = 5,
/// )
/// |> line(end = [10, 0])
/// |> line(end = [5, 0])
/// |> bezierCurve(
/// control1 = [-3, 0],
/// control2 = [2, 10],
/// end = [-5, 10],
/// )
/// |> line(end = [-4, 10])
/// |> line(end = [-5, -2])
/// |> close()
///
/// example = extrude(exampleSketch, length = 20, symmetric = true)
/// ```
///
/// ```no_run
/// exampleSketch = startSketchOn(XZ)
/// |> startProfile(at = [-10, 0])
/// |> arc(
/// angleStart = 120,
/// angleEnd = -60,
/// radius = 5,
/// )
/// |> line(end = [10, 0])
/// |> line(end = [5, 0])
/// |> bezierCurve(
/// control1 = [-3, 0],
/// control2 = [2, 10],
/// end = [-5, 10],
/// )
/// |> line(end = [-4, 10])
/// |> line(end = [-5, -2])
/// |> close()
///
/// example = extrude(exampleSketch, length = 10, bidirectionalLength = 50)
/// ```
#[stdlib {
name = "extrude",
feature_tree_operation = true,
unlabeled_first = true,
args = {
sketches = { docs = "Which sketch or sketches should be extruded"},
length = { docs = "How far to extrude the given sketches"},
symmetric = { docs = "If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch." },
bidirectional_length = { docs = "If specified, will also extrude in the opposite direction to 'distance' to the specified distance. If 'symmetric' is true, this value is ignored."},
tag_start = { docs = "A named tag for the face at the start of the extrusion, i.e. the original sketch" },
tag_end = { docs = "A named tag for the face at the end of the extrusion, i.e. the new face created by extruding the original sketch" },
},
tags = ["sketch"]
}]
#[allow(clippy::too_many_arguments)]
async fn inner_extrude(
sketches: Vec<Sketch>,

View File

@ -3,7 +3,6 @@
use std::num::NonZeroU32;
use anyhow::Result;
use kcl_derive_docs::stdlib;
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
use kittycad_modeling_cmds as kcmc;
@ -55,87 +54,6 @@ pub async fn loft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
Ok(KclValue::Solid { value })
}
/// Create a 3D surface or solid by interpolating between two or more sketches.
///
/// The sketches need to closed and on the same plane.
///
/// ```no_run
/// // Loft a square and a triangle.
/// squareSketch = startSketchOn(XY)
/// |> startProfile(at = [-100, 200])
/// |> line(end = [200, 0])
/// |> line(end = [0, -200])
/// |> line(end = [-200, 0])
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
/// |> close()
///
/// triangleSketch = startSketchOn(offsetPlane(XY, offset = 75))
/// |> startProfile(at = [0, 125])
/// |> line(end = [-15, -30])
/// |> line(end = [30, 0])
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
/// |> close()
///
/// loft([triangleSketch, squareSketch])
/// ```
///
/// ```no_run
/// // Loft a square, a circle, and another circle.
/// squareSketch = startSketchOn(XY)
/// |> startProfile(at = [-100, 200])
/// |> line(end = [200, 0])
/// |> line(end = [0, -200])
/// |> line(end = [-200, 0])
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
/// |> close()
///
/// circleSketch0 = startSketchOn(offsetPlane(XY, offset = 75))
/// |> circle( center = [0, 100], radius = 50 )
///
/// circleSketch1 = startSketchOn(offsetPlane(XY, offset = 150))
/// |> circle( center = [0, 100], radius = 20 )
///
/// loft([squareSketch, circleSketch0, circleSketch1])
/// ```
///
/// ```no_run
/// // Loft a square, a circle, and another circle with options.
/// squareSketch = startSketchOn(XY)
/// |> startProfile(at = [-100, 200])
/// |> line(end = [200, 0])
/// |> line(end = [0, -200])
/// |> line(end = [-200, 0])
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
/// |> close()
///
/// circleSketch0 = startSketchOn(offsetPlane(XY, offset = 75))
/// |> circle( center = [0, 100], radius = 50 )
///
/// circleSketch1 = startSketchOn(offsetPlane(XY, offset = 150))
/// |> circle( center = [0, 100], radius = 20 )
///
/// loft([squareSketch, circleSketch0, circleSketch1],
/// baseCurveIndex = 0,
/// bezApproximateRational = false,
/// tolerance = 0.000001,
/// vDegree = 2,
/// )
/// ```
#[stdlib {
name = "loft",
feature_tree_operation = true,
unlabeled_first = true,
args = {
sketches = {docs = "Which sketches to loft. Must include at least 2 sketches."},
v_degree = {docs = "Degree of the interpolation. Must be greater than zero. For example, use 2 for quadratic, or 3 for cubic interpolation in the V direction. This defaults to 2, if not specified."},
bez_approximate_rational = {docs = "Attempt to approximate rational curves (such as arcs) using a bezier. This will remove banding around interpolations between arcs and non-arcs. It may produce errors in other scenarios Over time, this field won't be necessary."},
base_curve_index = {docs = "This can be set to override the automatically determined topological base curve, which is usually the first section encountered."},
tolerance = {docs = "Tolerance for the loft operation."},
tag_start = { docs = "A named tag for the face at the start of the loft, i.e. the original sketch" },
tag_end = { docs = "A named tag for the face at the end of the loft, i.e. the last sketch" },
},
tags = ["sketch"]
}]
#[allow(clippy::too_many_arguments)]
async fn inner_loft(
sketches: Vec<Sketch>,

View File

@ -45,7 +45,6 @@ pub type StdFn = fn(
lazy_static! {
static ref CORE_FNS: Vec<Box<dyn StdLibFn>> = vec![
Box::new(crate::std::extrude::Extrude),
Box::new(crate::std::segment::SegEnd),
Box::new(crate::std::segment::SegEndX),
Box::new(crate::std::segment::SegEndY),
@ -57,8 +56,6 @@ lazy_static! {
Box::new(crate::std::segment::SegLen),
Box::new(crate::std::segment::SegAng),
Box::new(crate::std::segment::TangentToEnd),
Box::new(crate::std::shapes::CircleThreePoint),
Box::new(crate::std::shapes::Polygon),
Box::new(crate::std::sketch::InvoluteCircular),
Box::new(crate::std::sketch::Line),
Box::new(crate::std::sketch::XLine),
@ -75,12 +72,6 @@ lazy_static! {
Box::new(crate::std::sketch::TangentialArc),
Box::new(crate::std::sketch::BezierCurve),
Box::new(crate::std::sketch::Subtract2D),
Box::new(crate::std::patterns::PatternLinear2D),
Box::new(crate::std::patterns::PatternCircular2D),
Box::new(crate::std::sweep::Sweep),
Box::new(crate::std::loft::Loft),
Box::new(crate::std::assert::Assert),
Box::new(crate::std::assert::AssertIs)
];
}
@ -233,6 +224,14 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|e, a| Box::pin(crate::std::planes::offset_plane(e, a)),
StdFnProps::default("std::offsetPlane").include_in_feature_tree(),
),
("prelude", "assert") => (
|e, a| Box::pin(crate::std::assert::assert(e, a)),
StdFnProps::default("std::assert"),
),
("prelude", "assertIs") => (
|e, a| Box::pin(crate::std::assert::assert_is(e, a)),
StdFnProps::default("std::assertIs"),
),
("solid", "fillet") => (
|e, a| Box::pin(crate::std::fillet::fillet(e, a)),
StdFnProps::default("std::solid::fillet").include_in_feature_tree(),
@ -301,6 +300,10 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|e, a| Box::pin(crate::std::shapes::circle(e, a)),
StdFnProps::default("std::sketch::circle"),
),
("sketch", "extrude") => (
|e, a| Box::pin(crate::std::extrude::extrude(e, a)),
StdFnProps::default("std::sketch::extrude").include_in_feature_tree(),
),
("sketch", "patternTransform2d") => (
|e, a| Box::pin(crate::std::patterns::pattern_transform_2d(e, a)),
StdFnProps::default("std::sketch::patternTransform2d"),
@ -309,6 +312,22 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|e, a| Box::pin(crate::std::revolve::revolve(e, a)),
StdFnProps::default("std::sketch::revolve").include_in_feature_tree(),
),
("sketch", "sweep") => (
|e, a| Box::pin(crate::std::sweep::sweep(e, a)),
StdFnProps::default("std::sketch::sweep").include_in_feature_tree(),
),
("sketch", "loft") => (
|e, a| Box::pin(crate::std::loft::loft(e, a)),
StdFnProps::default("std::sketch::loft").include_in_feature_tree(),
),
("sketch", "polygon") => (
|e, a| Box::pin(crate::std::shapes::polygon(e, a)),
StdFnProps::default("std::sketch::polygon"),
),
("sketch", "circleThreePoint") => (
|e, a| Box::pin(crate::std::shapes::circle_three_point(e, a)),
StdFnProps::default("std::sketch::circleThreePoint"),
),
("sketch", "getCommonEdge") => (
|e, a| Box::pin(crate::std::edge::get_common_edge(e, a)),
StdFnProps::default("std::sketch::getCommonEdge"),
@ -325,6 +344,14 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|e, a| Box::pin(crate::std::edge::get_previous_adjacent_edge(e, a)),
StdFnProps::default("std::sketch::getPreviousAdjacentEdge"),
),
("sketch", "patternLinear2d") => (
|e, a| Box::pin(crate::std::patterns::pattern_linear_2d(e, a)),
StdFnProps::default("std::sketch::patternLinear2d"),
),
("sketch", "patternCircular2d") => (
|e, a| Box::pin(crate::std::patterns::pattern_circular_2d(e, a)),
StdFnProps::default("std::sketch::patternCircular2d"),
),
("appearance", "hexString") => (
|e, a| Box::pin(crate::std::appearance::hex_string(e, a)),
StdFnProps::default("std::appearance::hexString"),

View File

@ -3,7 +3,6 @@
use std::cmp::Ordering;
use anyhow::Result;
use kcl_derive_docs::stdlib;
use kcmc::{
each_cmd as mcmd, length_unit::LengthUnit, ok_response::OkModelingCmdResponse, shared::Transform,
websocket::OkWebSocketResponseData, ModelingCmd,
@ -546,47 +545,6 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
Ok(sketches.into())
}
/// Repeat a 2-dimensional sketch along some dimension, with a dynamic amount
/// of distance between each repetition, some specified number of times.
///
/// ```no_run
/// /// Pattern using a named axis.
///
/// exampleSketch = startSketchOn(XZ)
/// |> circle(center = [0, 0], radius = 1)
/// |> patternLinear2d(
/// axis = X,
/// instances = 7,
/// distance = 4
/// )
///
/// example = extrude(exampleSketch, length = 1)
/// ```
///
/// ```no_run
/// /// Pattern using a raw axis.
///
/// exampleSketch = startSketchOn(XZ)
/// |> circle(center = [0, 0], radius = 1)
/// |> patternLinear2d(
/// axis = [1, 0],
/// instances = 7,
/// distance = 4
/// )
///
/// example = extrude(exampleSketch, length = 1)
/// ```
#[stdlib {
name = "patternLinear2d",
unlabeled_first = true,
args = {
sketches = { docs = "The sketch(es) to duplicate" },
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect." },
distance = { docs = "Distance between each repetition. Also known as 'spacing'."},
axis = { docs = "The axis of the pattern. A 2D vector.", snippet_value_array = ["1", "0"] },
use_original = { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false." },
}
}]
async fn inner_pattern_linear_2d(
sketches: Vec<Sketch>,
instances: u32,
@ -810,40 +768,6 @@ pub async fn pattern_circular_2d(exec_state: &mut ExecState, args: Args) -> Resu
Ok(sketches.into())
}
/// Repeat a 2-dimensional sketch some number of times along a partial or
/// complete circle some specified number of times. Each object may
/// additionally be rotated along the circle, ensuring orientation of the
/// solid with respect to the center of the circle is maintained.
///
/// ```no_run
/// exampleSketch = startSketchOn(XZ)
/// |> startProfile(at = [.5, 25])
/// |> line(end = [0, 5])
/// |> line(end = [-1, 0])
/// |> line(end = [0, -5])
/// |> close()
/// |> patternCircular2d(
/// center = [0, 0],
/// instances = 13,
/// arcDegrees = 360,
/// rotateDuplicates = true
/// )
///
/// example = extrude(exampleSketch, length = 1)
/// ```
#[stdlib {
name = "patternCircular2d",
unlabeled_first = true,
args = {
sketch_set = { docs = "Which sketch(es) to pattern" },
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect."},
center = { docs = "The center about which to make the pattern. This is a 2D vector.", snippet_value_array = ["0", "0"]},
arc_degrees = { docs = "The arc angle (in degrees) to place the repetitions. Must be greater than 0. Defaults to 360."},
rotate_duplicates= { docs = "Whether or not to rotate the duplicates as they are copied. Defaults to true."},
use_original= { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false."},
},
tags = ["sketch"]
}]
#[allow(clippy::too_many_arguments)]
async fn inner_pattern_circular_2d(
sketch_set: Vec<Sketch>,

View File

@ -1,7 +1,6 @@
//! Standard library shapes.
use anyhow::Result;
use kcl_derive_docs::stdlib;
use kcmc::{
each_cmd as mcmd,
length_unit::LengthUnit,
@ -143,26 +142,6 @@ pub async fn circle_three_point(exec_state: &mut ExecState, args: Args) -> Resul
})
}
/// Construct a circle derived from 3 points.
///
/// ```no_run
/// exampleSketch = startSketchOn(XY)
/// |> circleThreePoint(p1 = [10,10], p2 = [20,8], p3 = [15,5])
/// |> extrude(length = 5)
/// ```
#[stdlib {
name = "circleThreePoint",
unlabeled_first = true,
args = {
sketch_surface_or_group = {docs = "Plane or surface to sketch on."},
p1 = {docs = "1st point to derive the circle."},
p2 = {docs = "2nd point to derive the circle."},
p3 = {docs = "3rd point to derive the circle."},
tag = {docs = "Identifier for the circle to reference elsewhere."},
},
tags = ["sketch"]
}]
// Similar to inner_circle, but needs to retain 3-point information in the
// path so it can be used for other features, otherwise it's lost.
async fn inner_circle_three_point(
@ -281,44 +260,6 @@ pub async fn polygon(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
})
}
/// Create a regular polygon with the specified number of sides that is either inscribed or circumscribed around a circle of the specified radius.
///
/// ```no_run
/// // Create a regular hexagon inscribed in a circle of radius 10
/// hex = startSketchOn(XY)
/// |> polygon(
/// radius = 10,
/// numSides = 6,
/// center = [0, 0],
/// inscribed = true,
/// )
///
/// example = extrude(hex, length = 5)
/// ```
///
/// ```no_run
/// // Create a square circumscribed around a circle of radius 5
/// square = startSketchOn(XY)
/// |> polygon(
/// radius = 5.0,
/// numSides = 4,
/// center = [10, 10],
/// inscribed = false,
/// )
/// example = extrude(square, length = 5)
/// ```
#[stdlib {
name = "polygon",
unlabeled_first = true,
args = {
sketch_surface_or_group = { docs = "Plane or surface to sketch on" },
radius = { docs = "The radius of the polygon", include_in_snippet = true },
num_sides = { docs = "The number of sides in the polygon", include_in_snippet = true },
center = { docs = "The center point of the polygon", snippet_value_array = ["0", "0"] },
inscribed = { docs = "Whether the polygon is inscribed (true, the default) or circumscribed (false) about a circle with the specified radius" },
},
tags = ["sketch"]
}]
#[allow(clippy::too_many_arguments)]
async fn inner_polygon(
sketch_surface_or_group: SketchOrSurface,

View File

@ -1,7 +1,6 @@
//! Standard library sweep.
use anyhow::Result;
use kcl_derive_docs::stdlib;
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
use kittycad_modeling_cmds::{self as kcmc, shared::RelativeTo};
use schemars::JsonSchema;
@ -56,122 +55,6 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
Ok(value.into())
}
/// Extrude a sketch along a path.
///
/// This, like extrude, is able to create a 3-dimensional solid from a
/// 2-dimensional sketch. However, unlike extrude, this creates a solid
/// by using the extent of the sketch as its path. This is useful for
/// creating more complex shapes that can't be created with a simple
/// extrusion.
///
/// You can provide more than one sketch to sweep, and they will all be
/// swept along the same path.
///
/// ```no_run
/// // Create a pipe using a sweep.
///
/// // Create a path for the sweep.
/// sweepPath = startSketchOn(XZ)
/// |> startProfile(at = [0.05, 0.05])
/// |> line(end = [0, 7])
/// |> tangentialArc(angle = 90, radius = 5)
/// |> line(end = [-3, 0])
/// |> tangentialArc(angle = -90, radius = 5)
/// |> line(end = [0, 7])
///
/// // Create a hole for the pipe.
/// pipeHole = startSketchOn(XY)
/// |> circle(
/// center = [0, 0],
/// radius = 1.5,
/// )
///
/// sweepSketch = startSketchOn(XY)
/// |> circle(
/// center = [0, 0],
/// radius = 2,
/// )
/// |> subtract2d(tool = pipeHole)
/// |> sweep(path = sweepPath)
/// ```
///
/// ```no_run
/// // Create a spring by sweeping around a helix path.
///
/// // Create a helix around the Z axis.
/// helixPath = helix(
/// angleStart = 0,
/// ccw = true,
/// revolutions = 4,
/// length = 10,
/// radius = 5,
/// axis = Z,
/// )
///
///
/// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(XZ)
/// |> circle( center = [5, 0], radius = 1)
/// |> sweep(path = helixPath)
/// ```
///
/// ```no_run
/// // Sweep two sketches along the same path.
///
/// sketch001 = startSketchOn(XY)
/// rectangleSketch = startProfile(sketch001, at = [-200, 23.86])
/// |> angledLine(angle = 0, length = 73.47, tag = $rectangleSegmentA001)
/// |> angledLine(
/// angle = segAng(rectangleSegmentA001) - 90,
/// length = 50.61,
/// )
/// |> angledLine(
/// angle = segAng(rectangleSegmentA001),
/// length = -segLen(rectangleSegmentA001),
/// )
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
/// |> close()
///
/// circleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)
///
/// sketch002 = startSketchOn(YZ)
/// sweepPath = startProfile(sketch002, at = [0, 0])
/// |> yLine(length = 231.81)
/// |> tangentialArc(radius = 80, angle = -90)
/// |> xLine(length = 384.93)
///
/// sweep([rectangleSketch, circleSketch], path = sweepPath)
/// ```
/// ```
/// // Sectionally sweep one sketch along the path
///
/// sketch001 = startSketchOn(XY)
/// circleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)
///
/// sketch002 = startSketchOn(YZ)
/// sweepPath = startProfile(sketch002, at = [0, 0])
/// |> yLine(length = 231.81)
/// |> tangentialArc(radius = 80, angle = -90)
/// |> xLine(length = 384.93)
///
/// sweep(circleSketch, path = sweepPath, sectional = true)
/// ```
#[stdlib {
name = "sweep",
feature_tree_operation = true,
unlabeled_first = true,
args = {
sketches = { docs = "The sketch or set of sketches that should be swept in space" },
path = { docs = "The path to sweep the sketch along" },
sectional = { docs = "If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components." },
tolerance = { docs = "Tolerance for this operation" },
relative_to = { docs = "What is the sweep relative to? Can be either 'sketchPlane' or 'trajectoryCurve'. Defaults to trajectoryCurve."},
tag_start = { docs = "A named tag for the face at the start of the sweep, i.e. the original sketch" },
tag_end = { docs = "A named tag for the face at the end of the sweep" },
},
tags = ["sketch"]
}]
#[allow(clippy::too_many_arguments)]
async fn inner_sweep(
sketches: Vec<Sketch>,