Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions content/pages/docs/kcl-lang/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@ two = increment(1)
three = add(1, delta = 2)
```

## Point-and-click editability

Custom functions are useful when the same parameterized feature needs to be
reused with different inputs. However, Zoo Design Studio's point-and-click
tools cannot currently edit sketches inside a custom function body.

Keep sketches at the top level when point-and-click editing is important. Use a
custom function when reuse is more important than point-and-click access to its
internal sketches. Repeating an identical body usually does not require a
custom function; use [`clone`](/docs/kcl-std/functions/std-clone),
[`patternLinear3d`](/docs/kcl-std/functions/std-solid-patternLinear3d), or
[`patternCircular3d`](/docs/kcl-std/functions/std-solid-patternCircular3d)
instead.

Below shows how a custom function must be called if it has a labeled argument, and another example with an unlabeled argument:

```
Expand Down
1 change: 1 addition & 0 deletions content/pages/docs/kcl-lang/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ things in a more tutorial fashion. See also our documentation of the [standard l
* [Arithmetic and logic](/docs/kcl-lang/arithmetic)
* [Values and types](/docs/kcl-lang/types)
* [Numeric types and units](/docs/kcl-lang/numeric)
* [Sketch blocks and constraints](/docs/kcl-lang/sketches)
* [Functions](/docs/kcl-lang/functions)
* [Arrays and ranges](/docs/kcl-lang/arrays)
* [Enums](/docs/kcl-lang/enums)
Expand Down
125 changes: 125 additions & 0 deletions content/pages/docs/kcl-lang/sketches.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
---
title: "Sketch Blocks and Constraints"
excerpt: "Write constrained, editable sketches with KCL sketch blocks."
layout: manual
---

KCL sketch blocks define 2D geometry and the relationships that control it.
Start a block on a base plane or supported face, add sketch geometry inside the
braces, then apply constraints to express design intent.

```kcl
width = 40mm
height = 24mm

profile = sketch(on = XY) {
bottom = line(start = [var 0mm, var 0mm], end = [var 40mm, var 0mm])
right = line(start = [var 40mm, var 0mm], end = [var 40mm, var 24mm])
top = line(start = [var 40mm, var 24mm], end = [var 0mm, var 24mm])
left = line(start = [var 0mm, var 24mm], end = [var 0mm, var 0mm])

coincident([bottom.end, right.start])
coincident([right.end, top.start])
coincident([top.end, left.start])
coincident([left.end, bottom.start])
horizontal(bottom)
horizontal(top)
vertical(right)
vertical(left)
horizontalDistance([bottom.start, bottom.end]) == width
verticalDistance([bottom.start, left.start]) == height
coincident([bottom.start, ORIGIN])
}
```

Inside a sketch block, the [solver module](/docs/kcl-std/modules/std-solver)
is automatically in scope. Call `line`, `arc`, `circle`, `coincident`, and
other solver functions without a `solver::` prefix.

## `var` values are initial guesses

A value introduced with `var` is a coordinate or size that the solver may
change. It is an initial guess, not a fixed dimension. Initial guesses must be
numeric literals, with a unit where applicable:

```kcl
exampleSketch = sketch(on = XY) {
// Valid initial guesses
samplePoint = point(at = [var 0mm, var -3mm])
coincident([samplePoint, [0mm, -3mm]])

// Invalid: identifiers and expressions cannot follow `var`
// samplePoint = point(at = [var width, var (height / 2)])
}
```

Put identifiers and expressions in constraints when they must drive the solved
result. For example, use `horizontalDistance([edge.start, edge.end]) == width`
rather than `end = [var width, var 0mm]`.

## Fully constrain the sketch

A fully constrained sketch has no unintended degrees of freedom. Use geometric
constraints such as `coincident`, `horizontal`, `vertical`, `parallel`,
`perpendicular`, `equalLength`, `tangent`, and `symmetric` to describe
relationships. Use dimensional constraints such as `distance`,
`horizontalDistance`, `verticalDistance`, `angle`, `radius`, and `diameter` for
driving values.

Anchor the profile once, then constrain the rest of the geometry relative to
that frame. Repeatedly fixing absolute points makes sketches harder to edit and
can over-constrain them. An under-constrained sketch can move in unintended
ways; an over-constrained sketch contains redundant or conflicting
relationships.

See the [constraint reference](/docs/kcl-std/modules/std-solver) for the full
API and the [sketching guide](/docs/zoo-design-studio/features/3d-design/parametric-modeling/sketching)
for the point-and-click workflow.

## Create a region from a closed profile

Most solid features consume a [region](/docs/kcl-std/functions/std-sketch-region)
created from a closed sketch boundary.

For one closed segment, such as a circle, pass that segment by itself. Omit
`intersectionIndex` and `direction`; they are unnecessary for one loop and are
intended to disambiguate a boundary traced from multiple segments.

```kcl
roundProfile = sketch(on = XY) {
perimeter = circle(start = [var 10mm, var 0mm], center = [var 0mm, var 0mm])
coincident([perimeter.center, ORIGIN])
radius(perimeter) == 10mm
horizontal([perimeter.center, perimeter.start])
}

roundRegion = region(segments = [roundProfile.perimeter])
roundBody = extrude(roundRegion, length = 5mm)
```

For a boundary traced from multiple intersecting segments, pass the first two
segments. Use `intersectionIndex` only when they intersect more than once, and
use `direction = CW` when the default counterclockwise trace selects the wrong
boundary.

If segment tracing cannot identify the intended boundary, use a point strictly
inside the region and provide its sketch:

```kcl
fallbackProfile = sketch(on = XY) {
perimeter = circle(start = [var 10mm, var 0mm], center = [var 0mm, var 0mm])
coincident([perimeter.center, ORIGIN])
radius(perimeter) == 10mm
horizontal([perimeter.center, perimeter.start])
}

fallbackRegion = region(point = [0mm, 0mm], sketch = fallbackProfile)
fallbackBody = extrude(fallbackRegion, length = 5mm)
```

## Control arc direction

An [arc](/docs/kcl-std/functions/std-solver-arc) sweeps counterclockwise from
its start point to its end point by default. If it takes the long or opposite
path, set `direction = CW`. Changing the direction is clearer than swapping
endpoints and then repairing every dependent constraint.
11 changes: 7 additions & 4 deletions content/pages/docs/kcl-samples/angle-gauge/main.kcl
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,13 @@ gaugeProfileSketch = sketch(on = XY) {
horizontal(cutoutTopEdge)
horizontal(cutoutAngleReferenceEdge)
vertical(cutoutLeftVerticalEdge)
angle([
cutoutAngleReferenceEdge,
cutoutAngledEdge
]) == cutoutAngle
angleDimension(
lines = [
cutoutAngleReferenceEdge,
cutoutAngledEdge
],
sector = 1,
) == cutoutAngle

horizontalDistance([cutoutTopEdge.start, cutoutTopEdge.end]) == cutoutDepth
verticalDistance([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ sketch007 = sketch(on = XY) {
line2 = line(start = [var 0.48in, var 0.75in], end = [var 0.53in, var 1.23in], construction = true)
coincident([line2.start, arc1.center])
coincident([line2.end, arc2.start])
angle([line2, line1]) == 6
angleDimension(lines = [line2, line1], sector = 1, labelPosition = [0.28in, 1.38in]) == 6
}

// Model the profile of the rear inlet bung, then sweep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ sketch003 = sketch(on = offsetPlane(XZ, offset = 0.4)) {
coincident([arc3.center, arc1.center])
tangent([arc1, arc2])
tangent([arc3, arc4])
angle([line2, line3]) == 85
angleDimension(lines = [line2, line3], labelPosition = [0.12in, 0.16in], sector = 3) == 85
distance([line2.start, line2.end]) == 1.4
coincident([line2.start, arc4.center])
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ sketch002 = sketch(on = XZ) {
coincident([line3.start, line1.start])
horizontal([line3.end, ORIGIN])
horizontal(line3)
angle([line1, line3]) == 45
angleDimension(lines = [line1, line3], sector = 1) == 45
distance([line1.start, line1.end]) == 1.65in
radius(arc1) == 3.2 / 2
circle1 = circle(start = [var 1.18in, var 0.93in], center = [var 0in, var 0in], construction = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ sketch002 = sketch(on = XZ) {
symmetric([line2, line1], axis = line3)
symmetric([arc2, arc1], axis = line3)
tangent([arc3, arc1])
angle([line1, line2]) == 180 - 30
angleDimension(lines = [line1, line2], sector = 2, labelPosition = [0in, 1.16in]) == 30
tangent([arc5, arc6])
verticalDistance([line3.start, arc2.center]) == 1
horizontalDistance([arc1.center, arc2.center]) == .1
Expand Down
2 changes: 1 addition & 1 deletion content/pages/docs/kcl-samples/axial-fan/fan-housing.kcl
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ sketch003 = sketch(on = XY) {
coincident([line6.end, line4.start])
equalLength([line5, line6])
verticalDistance([line5.start, line5.end]) == 6
angle([line3, line2]) == 60
angleDimension(lines = [line3, line2], sector = 4, labelPosition = [7.16mm, 24.34mm]) == 120deg
horizontalDistance([line1.start, arc4.center]) == 0.02mm
coincident([arc4.center, line4])
}
Expand Down
4 changes: 2 additions & 2 deletions content/pages/docs/kcl-samples/cycloidal-gear/main.kcl
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ outerProfile = sketch(on = XY) {

vertical(c2ToC1)
vertical(c1ToC6)
angle([baseline, c2ToC3]) == 150deg
angle([baseline, c4ToC5]) == 30deg
angleDimension(lines = [baseline, c2ToC3], sector = 3, labelPosition = [-1.23in, -0.83in]) == 150deg
angleDimension(lines = [baseline, c4ToC5], sector = 1, labelPosition = [-0.22in, 0.19in]) == 30deg
parallel([c2ToC3, c3ToC4])
parallel([c4ToC5, c5ToC6])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ bodyProfile = sketch(on = YZ) {
distance([line2.start, line2.end]) == plateLength
distance([line3.start, line3.end]) == width
distance([line6.start, line6.end]) == width
angle([line1, line2]) == plateAngle
angleDimension(lines = [line1, line2], sector = 1, labelPosition = [3.15mm, 3.38mm]) == plateAngle
}

bodyRegion = region(segments = [bodyProfile.line1, bodyProfile.line2])
Expand Down
2 changes: 1 addition & 1 deletion content/pages/docs/kcl-samples/m4-insert/main.kcl
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ insertProfileSketch = sketch(on = XY) {
horizontalDistance([midWall.start, centerGuide.end]) == insertGuideDiameter
horizontalDistance([bottomBridge.end, centerGuide.end]) == insertLowerBandDiameter / 2
verticalDistance([innerWall.end, innerWall.start]) == insertOverallHeight
angle([midWall, taperFlank]) == taperAngle
angleDimension(lines = [midWall, taperFlank], sector = 1, labelPosition = [-2.83mm, -6.02mm]) == taperAngle
}

insertProfileRegion = region(point = [-3.1475mm, -1.14mm], sketch = insertProfileSketch)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ sketch004 = sketch(on = XY) {
horizontal([line14.end, ORIGIN])
horizontal(line14)
horizontal(line13)
angle([line13, line5]) == 34
angleDimension(lines = [line13, line5], sector = 1) == 34
line15 = line(start = [var 0in, var 0in], end = [var 0in, var 4.46in], construction = true)
coincident([line15.start, line5.start])
vertical([line15.end, ORIGIN])
Expand All @@ -58,13 +58,13 @@ sketch004 = sketch(on = XY) {
vertical([line16.end, ORIGIN])
vertical(line16)
vertical(line15)
angle([line6, line15]) == 34
angle([line15, line7]) == 34
angle([line8, line14]) == 34
angle([line14, line9]) == 34
angle([line10, line16]) == 34
angle([line16, line11]) == 34
angle([line12, line13]) == 34
angleDimension(lines = [line6, line15], sector = 1) == 34
angleDimension(lines = [line15, line7], sector = 1) == 34
angleDimension(lines = [line8, line14], sector = 1) == 34
angleDimension(lines = [line14, line9], sector = 1) == 34
angleDimension(lines = [line10, line16], sector = 1) == 34
angleDimension(lines = [line16, line11], sector = 1) == 34
angleDimension(lines = [line12, line13], sector = 1) == 34
distance([line12.start, line12.end]) == 5.35
circle1 = circle(start = [var 4.54in, var 2.61in], center = [var 4.43in, var 2.99in])
coincident([circle1.center, line5.end])
Expand Down
2 changes: 1 addition & 1 deletion content/pages/docs/kcl-samples/wheel-hub/main.kcl
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ trimProfile = sketch(on = XY) {
startOffsetGuide.end
]) == rotorBore / 2
distance([line1.start, line1.end]) == lugSpacing / 3
angle([startOffsetGuide, line1]) == 10deg
angleDimension(lines = [startOffsetGuide, line1], sector = 1, labelPosition = [2.17in, -0.69in]) == 10deg
distance([line2.start, line2.end]) == hubSpacing * 1.5
}

Expand Down
4 changes: 2 additions & 2 deletions content/pages/docs/kcl-samples/zoo-logo/main.kcl
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ logoSketch = sketch(on = XY) {
parallel([o1Guide01, o1Guide02])
equalLength([o1Guide01, o1Guide02])

angle([frameBottom, o1Line01]) == 47deg
angleDimension(lines = [frameBottom, o1Line01], sector = 1, labelPosition = [1.06in, 0.06in]) == 47deg
horizontalDistance([frameLeft.end, o1GuideArc04.center]) == logoHeight * 1.448
verticalDistance([frameLeft.end, o1GuideArc04.center]) == logoHeight * 0.5
radius(o1Arc03) == logoHeight * 0.5
Expand Down Expand Up @@ -269,7 +269,7 @@ logoSketch = sketch(on = XY) {
horizontalDistance([o2Arc04.center, frameRight.start]) == logoHeight * 0.5
verticalDistance([frameRight.start, o2Arc04.center]) == logoHeight * 0.5
distance([o2GuideArc04.end, o2GuideArc04.start]) == logoHeight * 0.1
angle([frameBottom, o2Line03]) == 47deg
angleDimension(lines = [frameBottom, o2Line03], sector = 1, labelPosition = [2.2in, 0.04in]) == 47deg
radius(o2Arc02) == logoHeight * 0.5
radius(o2Arc03) == logoHeight * 0.27

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ patternTransform2d(
@sketches: [Sketch; 1+],
instances: number(_),
transform: fn(number(_)): { },
useOriginal?: boolean,
useOriginal?: bool,
): [Sketch; 1+]
```

Expand All @@ -25,7 +25,7 @@ patternTransform2d(
| `sketches` | [[`Sketch`](/docs/kcl-std/types/std-types-Sketch); 1+] | The sketch(es) to duplicate. | Yes |
| `instances` | [`number(_)`](/docs/kcl-std/types/std-types-number) | 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. | Yes |
| `transform` | [`fn(number(_)): { }`](/docs/kcl-std/types/std-types-fn) | How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples. | Yes |
| `useOriginal` | `boolean` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. | No |
| `useOriginal` | [`bool`](/docs/kcl-std/types/std-types-bool) | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. | No |

### Returns

Expand Down
8 changes: 6 additions & 2 deletions content/pages/docs/kcl-std/functions/std-sketch-region.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ segment from its start point to the intersection with the second segment,
then turning at each intersection using `direction` until returning to the
first segment.

For a single closed segment such as a circle, pass only that segment.
`intersectionIndex` and `direction` are unnecessary for one loop; use them
to disambiguate a boundary traced from multiple segments.

As a fallback, use the `point` parameter to select the closed boundary that
contains a given point. When using a 2D point rather than a point from the
sketch, provide the `sketch` parameter to specify which sketch the region is
Expand All @@ -37,8 +41,8 @@ refer to that point to create the region.
|----------|------|-------------|----------|
| `point` | [`Point2d`](/docs/kcl-std/types/std-types-Point2d) or [`Segment`](/docs/kcl-std/types/std-types-Segment) | A fallback point that is within the region's boundary. | No |
| `segments` | [[`Segment`](/docs/kcl-std/types/std-types-Segment); 1+] | The first two segments that form the region's boundary. In case of a circle, the one circle segment that forms the region. This is the preferred way to create a region. | No |
| `intersectionIndex` | [`number(_)`](/docs/kcl-std/types/std-types-number) | Index of the intersection of the first segment with the second segment to use as the region's boundary. The default is `-1`, which uses the last intersection. This is only used when the `segments` argument is provided. | No |
| `direction` | [`string`](/docs/kcl-std/types/std-types-string) | `CCW` for counterclockwise, `CW` for clockwise. Default is `CCW`. This is only used when the `segments` argument is provided. | No |
| `intersectionIndex` | [`number(_)`](/docs/kcl-std/types/std-types-number) | Index of the intersection of the first segment with the second segment to use as the region's boundary. The default is `-1`, which uses the last intersection. This is usually only needed when two or more `segments` are provided. | No |
| `direction` | [`string`](/docs/kcl-std/types/std-types-string) | `CCW` for counterclockwise, `CW` for clockwise. Default is `CCW`. This is usually only needed when two or more `segments` are provided. | No |
| `sketch` | [`any`](/docs/kcl-std/types/std-types-any) | The sketch that the region is from. This is required when point is a [`Point2d`](/docs/kcl-std/types/std-types-Point2d). | No |

### Returns
Expand Down
10 changes: 9 additions & 1 deletion content/pages/docs/kcl-std/functions/std-solver-angle.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ excerpt: "Constrain lines to meet at a given angle."
layout: manual
---

**WARNING:** This function is deprecated as of KCL 2.0.

Constrain lines to meet at a given angle.

```kcl
solver::angle(@input: [Segment; 2])
solver::angle(
@input: [Segment; 2],
labelPosition?: Point2d,
)
```

Deprecated as of KCL 2.0. Use `angleDimension` for new angle constraints.

The angle is measured counterclockwise from the first line to the second
line, modulo 180 degrees, so the order of the lines matters:
`angle([a, b]) == 30deg` is equivalent to `angle([b, a]) == 150deg`.
Expand All @@ -22,6 +29,7 @@ which end of each line segment is its start or end.
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `input` | [[`Segment`](/docs/kcl-std/types/std-types-Segment); 2] | The two line segments whose relative angle should match the value set with `==`, measured counterclockwise from the first line to the second, modulo 180 degrees. The order of the lines matters. | Yes |
| `labelPosition` | [`Point2d`](/docs/kcl-std/types/std-types-Point2d) | The desired position of the constraint label. | No |


### Examples
Expand Down
Loading
Loading