Skip to content

Commit

Permalink
Merge branch 'main' into reload-settings
Browse files Browse the repository at this point in the history
  • Loading branch information
lf94 authored Oct 4, 2024
2 parents 6e071a1 + ec8cacb commit 544e472
Show file tree
Hide file tree
Showing 14 changed files with 13,974 additions and 2,601 deletions.
30 changes: 26 additions & 4 deletions docs/kcl/reduce.md

Large diffs are not rendered by default.

16,281 changes: 13,724 additions & 2,557 deletions docs/kcl/std.json

Large diffs are not rendered by default.

96 changes: 96 additions & 0 deletions e2e/playwright/sketch-tests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,102 @@ sketch002 = startSketchOn(extrude001, 'END')
).toHaveAttribute('aria-pressed', 'true')
}).toPass({ timeout: 40_000, intervals: [1_000] })
})

test('Can sketch on face when user defined function was used in the sketch', async ({
page,
}) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })

// Checking for a regression that performs a sketch when a user defined function
// is declared at the top of the file and used in the sketch that is being drawn on.
// fn in2mm is declared at the top of the file and used rail which does a an extrusion with the function.

await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`fn in2mm = (inches) => {
return inches * 25.4
}
const railTop = in2mm(.748)
const railSide = in2mm(.024)
const railBaseWidth = in2mm(.612)
const railWideWidth = in2mm(.835)
const railBaseLength = in2mm(.200)
const railClampable = in2mm(.200)
const rail = startSketchOn('XZ')
|> startProfileAt([
-railTop / 2,
railClampable + railBaseLength
], %)
|> lineTo([
railTop / 2,
railClampable + railBaseLength
], %)
|> lineTo([
railWideWidth / 2,
railClampable / 2 + railBaseLength
], %, $seg01)
|> lineTo([railTop / 2, railBaseLength], %)
|> lineTo([railBaseWidth / 2, railBaseLength], %)
|> lineTo([railBaseWidth / 2, 0], %)
|> lineTo([-railBaseWidth / 2, 0], %)
|> lineTo([-railBaseWidth / 2, railBaseLength], %)
|> lineTo([-railTop / 2, railBaseLength], %)
|> lineTo([
-railWideWidth / 2,
railClampable / 2 + railBaseLength
], %)
|> lineTo([
-railTop / 2,
railClampable + railBaseLength
], %)
|> close(%)
|> extrude(in2mm(2), %)`
)
})

const center = { x: 600, y: 250 }
const rectangleSize = 20
await u.waitForAuthSkipAppStart()

// Start a sketch
await page.getByRole('button', { name: 'Start Sketch' }).click()

// Click the top face of this rail
await page.mouse.click(center.x, center.y)
await page.waitForTimeout(1000)

// Draw a rectangle
// top left
await page.mouse.click(center.x - rectangleSize, center.y - rectangleSize)
await page.waitForTimeout(250)
// top right
await page.mouse.click(center.x + rectangleSize, center.y - rectangleSize)
await page.waitForTimeout(250)

// bottom right
await page.mouse.click(center.x + rectangleSize, center.y + rectangleSize)
await page.waitForTimeout(250)

// bottom left
await page.mouse.click(center.x - rectangleSize, center.y + rectangleSize)
await page.waitForTimeout(250)

// top left
await page.mouse.click(center.x - rectangleSize, center.y - rectangleSize)
await page.waitForTimeout(250)

// exit sketch
await page.getByRole('button', { name: 'Exit Sketch' }).click()

// Check execution is done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
})
})

test2.describe('Sketch mode should be toleratant to syntax errors', () => {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 71 additions & 1 deletion openapi/machine-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,21 @@
],
"description": "Maximum part size that can be manufactured by this device. This may be some sort of theoretical upper bound, getting close to this limit seems like maybe a bad idea.\n\nThis may be `None` if the maximum size is not knowable by the Machine API.\n\nWhat \"close\" means is up to you!",
"nullable": true
},
"state": {
"allOf": [
{
"$ref": "#/components/schemas/MachineState"
}
],
"description": "Status of the printer -- be it printing, idle, or unreachable. This may dictate if a machine is capable of taking a new job."
}
},
"required": [
"id",
"machine_type",
"make_model"
"make_model",
"state"
],
"type": "object"
},
Expand All @@ -143,6 +152,67 @@
},
"type": "object"
},
"MachineState": {
"description": "Current state of the machine -- be it printing, idle or offline. This can be used to determine if a printer is in the correct state to take a new job.",
"oneOf": [
{
"description": "If a print state can not be resolved at this time, an Unknown may be returned.",
"enum": [
"Unknown"
],
"type": "string"
},
{
"description": "Idle, and ready for another job.",
"enum": [
"Idle"
],
"type": "string"
},
{
"description": "Running a job -- 3D printing or CNC-ing a part.",
"enum": [
"Running"
],
"type": "string"
},
{
"description": "Machine is currently offline or unreachable.",
"enum": [
"Offline"
],
"type": "string"
},
{
"description": "Job is underway but halted, waiting for some action to take place.",
"enum": [
"Paused"
],
"type": "string"
},
{
"description": "Job is finished, but waiting manual action to move back to Idle.",
"enum": [
"Complete"
],
"type": "string"
},
{
"additionalProperties": false,
"description": "The printer has failed and is in an unknown state that may require manual attention to resolve. The inner value is a human readable description of what specifically has failed.",
"properties": {
"Failed": {
"nullable": true,
"type": "string"
}
},
"required": [
"Failed"
],
"type": "object"
}
]
},
"MachineType": {
"description": "Specific technique by which this Machine takes a design, and produces a real-world 3D object.",
"oneOf": [
Expand Down
1 change: 1 addition & 0 deletions src/editor/plugins/lsp/kcl/highlight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { styleTags, tags as t } from '@lezer/highlight'

export const kclHighlight = styleTags({
'fn var let const': t.definitionKeyword,
'if else': t.controlKeyword,
return: t.controlKeyword,
'true false': t.bool,
nil: t.null,
Expand Down
1 change: 1 addition & 0 deletions src/editor/plugins/lsp/kcl/kcl.grammar
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ expression[@isGroup=Expression] {
} |
UnaryExpression { UnaryOp expression } |
ParenthesizedExpression { "(" expression ")" } |
IfExpression { kw<"if"> expression Body kw<"else"> Body } |
CallExpression { expression !call ArgumentList } |
ArrayExpression { "[" commaSep<expression | IntegerRange { expression !range ".." expression }> "]" } |
ObjectExpression { "{" commaSep<ObjectProperty> "}" } |
Expand Down
13 changes: 13 additions & 0 deletions src/lib/machine-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ export interface components {
*
* What "close" means is up to you! */
max_part_volume?: components['schemas']['Volume'] | null
/** @description Status of the printer -- be it printing, idle, or unreachable. This may dictate if a machine is capable of taking a new job. */
state: components['schemas']['MachineState']
}
/** @description Information regarding the make/model of a discovered endpoint. */
MachineMakeModel: {
Expand All @@ -136,6 +138,17 @@ export interface components {
/** @description The unique serial number of the connected Machine. */
serial?: string | null
}
/** @description Current state of the machine -- be it printing, idle or offline. This can be used to determine if a printer is in the correct state to take a new job. */
MachineState:
| 'Unknown'
| 'Idle'
| 'Running'
| 'Offline'
| 'Paused'
| 'Complete'
| {
Failed: string | null
}
/** @description Specific technique by which this Machine takes a design, and produces a real-world 3D object. */
MachineType: 'Stereolithography' | 'FusedDeposition' | 'Cnc'
/** @description The response from the `/ping` endpoint. */
Expand Down
14 changes: 8 additions & 6 deletions src/wasm-lib/kcl/src/docs/gen_std_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -787,12 +787,14 @@ fn test_generate_stdlib_json_schema() {
let stdlib = StdLib::new();
let combined = stdlib.combined();

let mut json_data = vec![];

for key in combined.keys().sorted() {
let internal_fn = combined.get(key).unwrap();
json_data.push(internal_fn.to_json().unwrap());
}
let json_data: Vec<_> = combined
.keys()
.sorted()
.map(|key| {
let internal_fn = combined.get(key).unwrap();
internal_fn.to_json().unwrap()
})
.collect();
expectorate::assert_contents(
"../../../docs/kcl/std.json",
&serde_json::to_string_pretty(&json_data).unwrap(),
Expand Down
2 changes: 2 additions & 0 deletions src/wasm-lib/kcl/src/docs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ impl StdLibFnArg {
return Ok(Some((index, format!("${{{}:{}}}", index, "myTag"))));
} else if self.type_ == "[KclValue]" && self.required {
return Ok(Some((index, format!("${{{}:{}}}", index, "[0..9]"))));
} else if self.type_ == "KclValue" && self.required {
return Ok(Some((index, format!("${{{}:{}}}", index, "3"))));
}
get_autocomplete_snippet_from_schema(&self.schema.schema.clone().into(), index)
}
Expand Down
65 changes: 32 additions & 33 deletions src/wasm-lib/kcl/src/std/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use serde_json::Value as JValue;
use super::{args::FromArgs, Args, FnAsArg};
use crate::{
errors::{KclError, KclErrorDetails},
executor::{ExecState, KclValue, Sketch, SourceRange, UserVal},
executor::{ExecState, KclValue, SourceRange, UserVal},
function_param::FunctionParam,
};

Expand Down Expand Up @@ -98,17 +98,24 @@ async fn call_map_closure<'a>(

/// For each item in an array, update a value.
pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (array, start, f): (Vec<u64>, Sketch, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?;
let (array, start, f): (Vec<JValue>, KclValue, FnAsArg<'_>) = FromArgs::from_args(&args, 0)?;
let array: Vec<KclValue> = array
.into_iter()
.map(|jval| {
KclValue::UserVal(UserVal {
value: jval,
meta: vec![args.source_range.into()],
})
})
.collect();
let reduce_fn = FunctionParam {
inner: f.func,
fn_expr: f.expr,
meta: vec![args.source_range.into()],
ctx: args.ctx.clone(),
memory: *f.memory,
};
inner_reduce(array, start, reduce_fn, exec_state, &args)
.await
.map(|sg| KclValue::UserVal(UserVal::new(sg.meta.clone(), sg)))
inner_reduce(array, start, reduce_fn, exec_state, &args).await
}

/// Take a starting value. Then, for each element of an array, calculate the next value,
Expand All @@ -125,60 +132,52 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// }
/// decagon(5.0) |> close(%)
/// ```
/// ```no_run
/// array = [1, 2, 3]
/// sum = reduce(array, 0, (i, result_so_far) => { return i + result_so_far })
/// assertEqual(sum, 6, 0.00001, "1 + 2 + 3 summed is 6")
/// ```
/// ```no_run
/// fn add = (a, b) => { return a + b }
/// fn sum = (array) => { return reduce(array, 0, add) }
/// assertEqual(sum([1, 2, 3]), 6, 0.00001, "1 + 2 + 3 summed is 6")
/// ```
#[stdlib {
name = "reduce",
}]
async fn inner_reduce<'a>(
array: Vec<u64>,
start: Sketch,
array: Vec<KclValue>,
start: KclValue,
reduce_fn: FunctionParam<'a>,
exec_state: &mut ExecState,
args: &'a Args,
) -> Result<Sketch, KclError> {
) -> Result<KclValue, KclError> {
let mut reduced = start;
for i in array {
reduced = call_reduce_closure(i, reduced, &reduce_fn, args.source_range, exec_state).await?;
for elem in array {
reduced = call_reduce_closure(elem, reduced, &reduce_fn, args.source_range, exec_state).await?;
}

Ok(reduced)
}

async fn call_reduce_closure<'a>(
i: u64,
start: Sketch,
elem: KclValue,
start: KclValue,
reduce_fn: &FunctionParam<'a>,
source_range: SourceRange,
exec_state: &mut ExecState,
) -> Result<Sketch, KclError> {
) -> Result<KclValue, KclError> {
// Call the reduce fn for this repetition.
let reduce_fn_args = vec![
KclValue::UserVal(UserVal {
value: serde_json::Value::Number(i.into()),
meta: vec![source_range.into()],
}),
KclValue::new_user_val(start.meta.clone(), start),
];
let reduce_fn_args = vec![elem, start];
let transform_fn_return = reduce_fn.call(exec_state, reduce_fn_args).await?;

// Unpack the returned transform object.
let source_ranges = vec![source_range];
let closure_retval = transform_fn_return.ok_or_else(|| {
let out = transform_fn_return.ok_or_else(|| {
KclError::Semantic(KclErrorDetails {
message: "Reducer function must return a value".to_string(),
source_ranges: source_ranges.clone(),
})
})?;
let Some(out) = closure_retval.as_user_val() else {
return Err(KclError::Semantic(KclErrorDetails {
message: "Reducer function must return a UserValue".to_string(),
source_ranges: source_ranges.clone(),
}));
};
let Some((out, _meta)) = out.get() else {
return Err(KclError::Semantic(KclErrorDetails {
message: "Reducer function must return a Sketch".to_string(),
source_ranges: source_ranges.clone(),
}));
};
Ok(out)
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 544e472

Please sign in to comment.