2025-05-06 11:02:55 +12:00
/// The KCL standard library
///
/// Contains frequently used constants, functions for interacting with the KittyCAD servers to
/// create sketches and geometry, and utility functions.
2025-05-11 19:32:33 +12:00
///
/// The standard library is organised into modules (listed below), but most things are always available
/// in KCL programs.
2025-05-12 12:59:45 +12:00
///
2025-05-20 07:49:23 +12:00
/// You might also want the [KCL language reference](/docs/kcl-lang) or the [KCL guide](https://zoo.dev/docs/kcl-book/intro.html).
2025-05-06 11:02:55 +12:00
2025-02-25 16:10:06 +13:00
@no_std
2025-05-06 08:44:03 +12:00
@settings(defaultLengthUnit = mm, kclVersion = 1.0)
2025-02-25 16:10:06 +13:00
// Note that everything in the prelude is treated as exported.
2025-04-22 11:00:53 +12:00
export import * from "std::types"
2025-04-29 08:41:31 +12:00
export import "std::units"
2025-05-06 16:09:59 +12:00
export import * from "std::array"
2025-04-30 11:07:05 -04:00
export import * from "std::math"
2025-03-24 21:55:24 +13:00
export import * from "std::sketch"
2025-04-28 14:20:38 +12:00
export import * from "std::solid"
2025-05-06 16:09:59 +12:00
export import * from "std::transform"
2025-03-30 11:10:44 +13:00
export import "std::turns"
2025-05-15 12:13:04 -05:00
export import "std::sweep"
2025-05-23 13:53:58 -05:00
export import "std::appearance"
2025-02-20 19:33:21 +13:00
2025-05-11 19:32:33 +12:00
/// An abstract 3d plane aligned with the X and Y axes. Its normal is the positive Z axis.
2025-02-27 15:58:58 +13:00
export XY = {
origin = { x = 0, y = 0, z = 0 },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 1, z = 0 },
}: Plane
2025-05-11 19:32:33 +12:00
/// An abstract 3d plane aligned with the X and Z axes. Its normal is the negative Y axis.
2025-02-27 15:58:58 +13:00
export XZ = {
origin = { x = 0, y = 0, z = 0 },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 0, z = 1 },
}: Plane
2025-05-11 19:32:33 +12:00
/// An abstract 3d plane aligned with the Y and Z axes. Its normal is the positive X axis.
2025-02-27 15:58:58 +13:00
export YZ = {
origin = { x = 0, y = 0, z = 0 },
xAxis = { x = 0, y = 1, z = 0 },
yAxis = { x = 0, y = 0, z = 1 },
}: Plane
2025-04-03 22:44:52 +13:00
2025-05-11 19:32:33 +12:00
/// The X-axis (can be used in both 2d and 3d contexts).
2025-04-03 22:44:52 +13:00
export X = {
origin = [0, 0, 0],
direction = [1, 0, 0],
}: Axis3d
2025-05-11 19:32:33 +12:00
/// The Y-axis (can be used in both 2d and 3d contexts).
2025-04-03 22:44:52 +13:00
export Y = {
origin = [0, 0, 0],
direction = [0, 1, 0],
}: Axis3d
2025-05-11 19:32:33 +12:00
/// The 3D Z-axis.
2025-04-03 22:44:52 +13:00
export Z = {
origin = [0, 0, 0],
direction = [0, 0, 1],
}: Axis3d
2025-04-14 20:37:45 +12:00
/// Identifies the starting face of an extrusion. I.e., the face which is extruded.
2025-06-16 09:10:36 +12:00
export START = 'start': TaggedFace
2025-04-14 20:37:45 +12:00
/// Identifies the ending face of an extrusion. I.e., the new face created by an extrusion.
2025-06-16 09:10:36 +12:00
export END = 'end': TaggedFace
2025-04-14 20:37:45 +12:00
2025-04-03 22:44:52 +13:00
/// Create a helix.
///
/// ```
/// // Create a helix around the Z axis.
/// helixPath = helix(
/// angleStart = 0,
/// ccw = true,
/// revolutions = 5,
/// length = 10,
/// radius = 5,
/// axis = Z,
/// )
///
/// // Create a spring by sweeping around the helix path.
2025-05-16 03:16:32 +01:00
/// springSketch = startSketchOn(XZ)
/// |> circle( center = [5, 0], radius = 0.5)
/// |> sweep(path = helixPath)
2025-04-03 22:44:52 +13:00
/// ```
///
/// ```
/// // Create a helix around an edge.
/// helper001 = startSketchOn(XZ)
2025-04-25 16:01:35 -05:00
/// |> startProfile(at = [0, 0])
2025-04-03 22:44:52 +13:00
/// |> line(end = [0, 10], tag = $edge001)
///
/// helixPath = helix(
/// angleStart = 0,
/// ccw = true,
/// revolutions = 5,
/// length = 10,
/// radius = 5,
/// axis = edge001,
/// )
///
/// // Create a spring by sweeping around the helix path.
2025-05-16 03:16:32 +01:00
/// springSketch = startSketchOn(XZ)
/// |> circle( center = [5, 0], radius = 0.5 )
/// |> sweep(path = helixPath)
2025-04-03 22:44:52 +13:00
/// ```
///
/// ```
/// // Create a helix around a custom axis.
/// helixPath = helix(
/// angleStart = 0,
/// ccw = true,
/// revolutions = 5,
/// length = 10,
/// radius = 5,
/// axis = {
/// direction = [0, 0, 1.0],
/// origin = [0, 0.25, 0]
/// }
/// )
///
/// // Create a spring by sweeping around the helix path.
2025-05-16 03:16:32 +01:00
/// springSketch = startSketchOn(XZ)
/// |> circle( center = [5, 0], radius = 1 )
/// |> sweep(path = helixPath)
2025-04-03 22:44:52 +13:00
/// ```
///
/// ```
/// // Create a helix on a cylinder.
///
/// part001 = startSketchOn(XY)
2025-04-30 13:12:40 +12:00
/// |> circle( center = [5, 5], radius= 10 )
2025-04-03 22:44:52 +13:00
/// |> extrude(length = 10)
///
/// helix(
/// angleStart = 0,
/// ccw = true,
/// revolutions = 16,
/// cylinder = part001,
/// )
/// ```
@(impl = std_rust)
export fn helix(
/// Number of revolutions.
revolutions: number(_),
2025-04-23 10:58:35 +12:00
/// Start angle.
2025-04-14 05:58:19 -04:00
angleStart: number(Angle),
2025-04-03 22:44:52 +13:00
/// Is the helix rotation counter clockwise? The default is `false`.
ccw?: bool,
/// Radius of the helix.
2025-05-29 07:15:04 +12:00
@(includeInSnippet = true)
2025-04-14 05:58:19 -04:00
radius?: number(Length),
2025-04-03 22:44:52 +13:00
/// Axis to use for the helix.
2025-05-29 07:15:04 +12:00
@(includeInSnippet = true)
2025-04-03 22:44:52 +13:00
axis?: Axis3d | Edge,
/// Length of the helix. This is not necessary if the helix is created around an edge. If not given the length of the edge is used.
2025-05-29 07:15:04 +12:00
@(includeInSnippet = true)
2025-04-14 05:58:19 -04:00
length?: number(Length),
2025-04-03 22:44:52 +13:00
/// Cylinder to create the helix on.
cylinder?: Solid,
): Helix {}
2025-04-24 22:01:27 +12:00
/// Offset a plane by a distance along its normal.
///
/// For example, if you offset the `XZ` plane by 10, the new plane will be parallel to the `XZ`
/// plane and 10 units away from it.
///
/// ```
/// // Loft a square and a circle on the `XY` plane using offset.
/// squareSketch = startSketchOn(XY)
2025-04-25 16:01:35 -05:00
/// |> startProfile(at = [-100, 200])
2025-04-24 22:01:27 +12:00
/// |> line(end = [200, 0])
/// |> line(end = [0, -200])
/// |> line(end = [-200, 0])
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
/// |> close()
///
/// circleSketch = startSketchOn(offsetPlane(XY, offset = 150))
/// |> circle( center = [0, 100], radius = 50 )
///
/// loft([squareSketch, circleSketch])
/// ```
///
/// ```
/// // Loft a square and a circle on the `XZ` plane using offset.
/// squareSketch = startSketchOn(XZ)
2025-04-25 16:01:35 -05:00
/// |> startProfile(at = [-100, 200])
2025-04-24 22:01:27 +12:00
/// |> line(end = [200, 0])
/// |> line(end = [0, -200])
/// |> line(end = [-200, 0])
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
/// |> close()
///
/// circleSketch = startSketchOn(offsetPlane(XZ, offset = 150))
/// |> circle( center = [0, 100], radius = 50 )
///
/// loft([squareSketch, circleSketch])
/// ```
///
/// ```
/// // Loft a square and a circle on the `YZ` plane using offset.
/// squareSketch = startSketchOn(YZ)
2025-04-25 16:01:35 -05:00
/// |> startProfile(at = [-100, 200])
2025-04-24 22:01:27 +12:00
/// |> line(end = [200, 0])
/// |> line(end = [0, -200])
/// |> line(end = [-200, 0])
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
/// |> close()
///
/// circleSketch = startSketchOn(offsetPlane(YZ, offset = 150))
/// |> circle( center = [0, 100], radius = 50 )
///
/// loft([squareSketch, circleSketch])
/// ```
///
/// ```
/// // Loft a square and a circle on the `-XZ` plane using offset.
/// squareSketch = startSketchOn(-XZ)
2025-04-25 16:01:35 -05:00
/// |> startProfile(at = [-100, 200])
2025-04-24 22:01:27 +12:00
/// |> line(end = [200, 0])
/// |> line(end = [0, -200])
/// |> line(end = [-200, 0])
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
/// |> close()
///
/// circleSketch = startSketchOn(offsetPlane(-XZ, offset = 150))
/// |> circle(center = [0, 100], radius = 50)
///
/// loft([squareSketch, circleSketch])
/// ```
///
/// ```
/// // A circle on the XY plane
/// startSketchOn(XY)
2025-04-25 16:01:35 -05:00
/// |> startProfile(at = [0, 0])
2025-04-24 22:01:27 +12:00
/// |> circle( radius = 10, center = [0, 0] )
///
/// // Triangle on the plane 4 units above
/// startSketchOn(offsetPlane(XY, offset = 4))
2025-04-25 16:01:35 -05:00
/// |> startProfile(at = [0, 0])
2025-04-24 22:01:27 +12:00
/// |> line(end = [10, 0])
/// |> line(end = [0, 10])
/// |> close()
/// ```
@(impl = std_rust)
export fn offsetPlane(
/// The plane (e.g. `XY`) which this new plane is created from.
@plane: Plane,
/// Distance from the standard plane this new plane will be created at.
offset: number(Length),
): Plane {}
2025-05-13 08:29:38 +12:00
/// Clone a sketch or solid.
///
/// This works essentially like a copy-paste operation. It creates a perfect replica
/// at that point in time that you can manipulate individually afterwards.
///
/// This doesn't really have much utility unless you need the equivalent of a double
/// instance pattern with zero transformations.
///
/// Really only use this function if YOU ARE SURE you need it. In most cases you
/// do not need clone and using a pattern with `instance = 2` is more appropriate.
///
/// ```kcl
/// // Clone a basic sketch and move it and extrude it.
/// exampleSketch = startSketchOn(XY)
/// |> startProfile(at = [0, 0])
/// |> line(end = [10, 0])
/// |> line(end = [0, 10])
/// |> line(end = [-10, 0])
/// |> close()
///
/// clonedSketch = clone(exampleSketch)
/// |> scale(
/// x = 1.0,
/// y = 1.0,
/// z = 2.5,
/// )
/// |> translate(
/// x = 15.0,
/// y = 0,
/// z = 0,
/// )
/// |> extrude(length = 5)
/// ```
///
/// ```kcl
/// // Clone a basic solid and move it.
///
/// exampleSketch = startSketchOn(XY)
/// |> startProfile(at = [0, 0])
/// |> line(end = [10, 0])
/// |> line(end = [0, 10])
/// |> line(end = [-10, 0])
/// |> close()
///
/// myPart = extrude(exampleSketch, length = 5)
/// clonedPart = clone(myPart)
/// |> translate(
/// x = 25.0,
/// )
/// ```
///
/// ```kcl
/// // Translate and rotate a cloned sketch to create a loft.
///
/// sketch001 = startSketchOn(XY)
/// |> startProfile(at = [-10, 10])
/// |> xLine(length = 20)
/// |> yLine(length = -20)
/// |> xLine(length = -20)
/// |> close()
///
/// sketch002 = clone(sketch001)
/// |> translate(x = 0, y = 0, z = 20)
/// |> rotate(axis = [0, 0, 1.0], angle = 45)
///
/// loft([sketch001, sketch002])
/// ```
///
/// ```kcl
/// // Translate a cloned solid. Fillet only the clone.
///
/// sketch001 = startSketchOn(XY)
/// |> startProfile(at = [-10, 10])
/// |> xLine(length = 20)
/// |> yLine(length = -20)
/// |> xLine(length = -20, tag = $filletTag)
/// |> close()
/// |> extrude(length = 5)
///
///
/// sketch002 = clone(sketch001)
/// |> translate(x = 0, y = 0, z = 20)
/// |> fillet(
/// radius = 2,
/// tags = [getNextAdjacentEdge(filletTag)],
/// )
/// ```
///
/// ```kcl
/// // You can reuse the tags from the original geometry with the cloned geometry.
///
/// sketch001 = startSketchOn(XY)
/// |> startProfile(at = [0, 0])
/// |> line(end = [10, 0])
/// |> line(end = [0, 10], tag = $sketchingFace)
/// |> line(end = [-10, 0])
/// |> close()
///
/// sketch002 = clone(sketch001)
/// |> translate(x = 10, y = 20, z = 0)
/// |> extrude(length = 5)
///
/// startSketchOn(sketch002, face = sketchingFace)
/// |> startProfile(at = [1, 1])
/// |> line(end = [8, 0])
/// |> line(end = [0, 8])
/// |> line(end = [-8, 0])
/// |> close(tag = $sketchingFace002)
/// |> extrude(length = 10)
/// ```
///
/// ```kcl
/// // You can also use the tags from the original geometry to fillet the cloned geometry.
///
/// width = 20
/// length = 10
/// thickness = 1
/// filletRadius = 2
///
/// mountingPlateSketch = startSketchOn(XY)
/// |> startProfile(at = [-width/2, -length/2])
/// |> line(endAbsolute = [width/2, -length/2], tag = $edge1)
/// |> line(endAbsolute = [width/2, length/2], tag = $edge2)
/// |> line(endAbsolute = [-width/2, length/2], tag = $edge3)
/// |> close(tag = $edge4)
///
/// mountingPlate = extrude(mountingPlateSketch, length = thickness)
///
/// clonedMountingPlate = clone(mountingPlate)
/// |> fillet(
/// radius = filletRadius,
/// tags = [
/// getNextAdjacentEdge(edge1),
/// getNextAdjacentEdge(edge2),
/// getNextAdjacentEdge(edge3),
/// getNextAdjacentEdge(edge4)
/// ],
/// )
/// |> translate(x = 0, y = 50, z = 0)
/// ```
///
/// ```kcl
/// // Create a spring by sweeping around a helix path from a cloned sketch.
///
/// // Create a helix around the Z axis.
/// helixPath = helix(
/// angleStart = 0,
/// ccw = true,
/// revolutions = 4,
/// length = 10,
/// radius = 5,
/// axis = Z,
/// )
///
///
2025-05-16 03:16:32 +01:00
/// springSketch = startSketchOn(XZ)
2025-05-13 08:29:38 +12:00
/// |> circle( center = [0, 0], radius = 1)
///
/// // Create a spring by sweeping around the helix path.
/// sweepedSpring = clone(springSketch)
2025-05-16 03:16:32 +01:00
/// |> translate(x=5)
/// |> sweep(path = helixPath)
2025-05-13 08:29:38 +12:00
/// ```
///
/// ```kcl
/// // A donut shape from a cloned sketch.
/// sketch001 = startSketchOn(XY)
/// |> circle( center = [15, 0], radius = 5 )
///
/// sketch002 = clone(sketch001)
/// |> translate( z = 30)
/// |> revolve(
/// angle = 360,
/// axis = Y,
/// )
/// ```
///
/// ```kcl
/// // Sketch on the end of a revolved face by tagging the end face.
/// // This shows the cloned geometry will have the same tags as the original geometry.
///
/// exampleSketch = startSketchOn(XY)
/// |> startProfile(at = [4, 12])
/// |> line(end = [2, 0])
/// |> line(end = [0, -6])
/// |> line(end = [4, -6])
/// |> line(end = [0, -6])
/// |> line(end = [-3.75, -4.5])
/// |> line(end = [0, -5.5])
/// |> line(end = [-2, 0])
/// |> close()
///
/// example001 = revolve(exampleSketch, axis = Y, angle = 180, tagEnd = $end01)
///
/// // example002 = clone(example001)
/// // |> translate(x = 0, y = 20, z = 0)
///
/// // Sketch on the cloned face.
/// // exampleSketch002 = startSketchOn(example002, face = end01)
/// // |> startProfile(at = [4.5, -5])
/// // |> line(end = [0, 5])
/// // |> line(end = [5, 0])
/// // |> line(end = [0, -5])
/// // |> close()
///
/// // example003 = extrude(exampleSketch002, length = 5)
/// ```
///
/// ```kcl
/// // Clone an imported model.
///
/// import "tests/inputs/cube.sldprt" as cube
///
/// myCube = cube
///
/// clonedCube = clone(myCube)
/// |> translate(
/// x = 1020,
/// )
/// |> appearance(
/// color = "#ff0000",
/// metalness = 50,
/// roughness = 50
/// )
/// ```
@(impl = std_rust)
export fn clone(
/// The sketch, solid, or imported geometry to be cloned.
@geometry: Sketch | Solid | ImportedGeometry,
): Sketch | Solid | ImportedGeometry {}
2025-05-30 11:00:16 +12:00
/// Asserts that a value is the boolean value true.
///
/// ```kcl,norun
/// kclIsFun = true
/// assertIs(kclIsFun)
/// ```
@(impl = std_rust)
export fn assertIs(
/// Value to check. If this is the boolean value true, assert passes. Otherwise it fails..
@actual: bool,
/// If the value was false, the program will terminate with this error message
error?: string,
) {}
/// 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.
///
/// ```kcl,norun
/// 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")
/// ```
@(impl = std_rust)
export fn assert(
/// Value to check. If this is the boolean value true, assert passes. Otherwise it fails..
@actual: number,
/// Comparison argument. If given, checks the `actual` value is greater than this.
isGreaterThan?: number,
/// Comparison argument. If given, checks the `actual` value is less than this.
isLessThan?: number,
/// Comparison argument. If given, checks the `actual` value is greater than or equal to this.
isGreaterThanOrEqual?: number,
/// Comparison argument. If given, checks the `actual` value is less than or equal to this.
isLessThanOrEqual?: number,
/// Comparison argument. If given, checks the `actual` value is less than or equal to this.
@(includeInSnippet = true)
isEqualTo?: number,
/// 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.
tolerance?: number,
/// If the value was false, the program will terminate with this error message
error?: string,
) {}