holes (#848)
* start of holes Signed-off-by: Jess Frazelle <github@jessfraz.com> * update docs Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates; Signed-off-by: Jess Frazelle <github@jessfraz.com> * close it Signed-off-by: Jess Frazelle <github@jessfraz.com> * Fix holes in jess's branch (#857) tweak * holes Signed-off-by: Jess Frazelle <github@jessfraz.com> * bump version Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix image Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com> Co-authored-by: mlfarrell <michael@kittycad.io>
This commit is contained in:
		
							
								
								
									
										1327
									
								
								docs/kcl/std.json
									
									
									
									
									
								
							
							
						
						
									
										1327
									
								
								docs/kcl/std.json
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										214
									
								
								docs/kcl/std.md
									
									
									
									
									
								
							
							
						
						
									
										214
									
								
								docs/kcl/std.md
									
									
									
									
									
								
							@ -26,6 +26,7 @@
 | 
			
		||||
	* [`extrude`](#extrude)
 | 
			
		||||
	* [`floor`](#floor)
 | 
			
		||||
	* [`getExtrudeWallTransform`](#getExtrudeWallTransform)
 | 
			
		||||
	* [`hole`](#hole)
 | 
			
		||||
	* [`lastSegX`](#lastSegX)
 | 
			
		||||
	* [`lastSegY`](#lastSegY)
 | 
			
		||||
	* [`legAngX`](#legAngX)
 | 
			
		||||
@ -2023,6 +2024,219 @@ getExtrudeWallTransform(surface_name: string, extrude_group: ExtrudeGroup) -> Ex
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### hole
 | 
			
		||||
 | 
			
		||||
Use a sketch to cut a hole in another sketch.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
hole(hole_sketch_group: SketchGroup, sketch_group: SketchGroup) -> SketchGroup
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Arguments
 | 
			
		||||
 | 
			
		||||
* `hole_sketch_group`: `SketchGroup` - A sketch group is a collection of paths.
 | 
			
		||||
```
 | 
			
		||||
{
 | 
			
		||||
	// The id of the sketch group.
 | 
			
		||||
	id: uuid,
 | 
			
		||||
	// The plane id of the sketch group.
 | 
			
		||||
	planeId: uuid,
 | 
			
		||||
	// The position of the sketch group.
 | 
			
		||||
	position: [number, number, number],
 | 
			
		||||
	// The rotation of the sketch group.
 | 
			
		||||
	rotation: [number, number, number, number],
 | 
			
		||||
	// The starting path.
 | 
			
		||||
	start: {
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
},
 | 
			
		||||
	// The paths in the sketch group.
 | 
			
		||||
	value: [{
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
	type: string,
 | 
			
		||||
} |
 | 
			
		||||
{
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
	type: string,
 | 
			
		||||
	// The x coordinate.
 | 
			
		||||
	x: number,
 | 
			
		||||
} |
 | 
			
		||||
{
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
	type: string,
 | 
			
		||||
	// The x coordinate.
 | 
			
		||||
	x: number,
 | 
			
		||||
	// The y coordinate.
 | 
			
		||||
	y: number,
 | 
			
		||||
} |
 | 
			
		||||
{
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
	type: string,
 | 
			
		||||
}],
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths.
 | 
			
		||||
```
 | 
			
		||||
{
 | 
			
		||||
	// The id of the sketch group.
 | 
			
		||||
	id: uuid,
 | 
			
		||||
	// The plane id of the sketch group.
 | 
			
		||||
	planeId: uuid,
 | 
			
		||||
	// The position of the sketch group.
 | 
			
		||||
	position: [number, number, number],
 | 
			
		||||
	// The rotation of the sketch group.
 | 
			
		||||
	rotation: [number, number, number, number],
 | 
			
		||||
	// The starting path.
 | 
			
		||||
	start: {
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
},
 | 
			
		||||
	// The paths in the sketch group.
 | 
			
		||||
	value: [{
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
	type: string,
 | 
			
		||||
} |
 | 
			
		||||
{
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
	type: string,
 | 
			
		||||
	// The x coordinate.
 | 
			
		||||
	x: number,
 | 
			
		||||
} |
 | 
			
		||||
{
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
	type: string,
 | 
			
		||||
	// The x coordinate.
 | 
			
		||||
	x: number,
 | 
			
		||||
	// The y coordinate.
 | 
			
		||||
	y: number,
 | 
			
		||||
} |
 | 
			
		||||
{
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
	type: string,
 | 
			
		||||
}],
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Returns
 | 
			
		||||
 | 
			
		||||
* `SketchGroup` - A sketch group is a collection of paths.
 | 
			
		||||
```
 | 
			
		||||
{
 | 
			
		||||
	// The id of the sketch group.
 | 
			
		||||
	id: uuid,
 | 
			
		||||
	// The plane id of the sketch group.
 | 
			
		||||
	planeId: uuid,
 | 
			
		||||
	// The position of the sketch group.
 | 
			
		||||
	position: [number, number, number],
 | 
			
		||||
	// The rotation of the sketch group.
 | 
			
		||||
	rotation: [number, number, number, number],
 | 
			
		||||
	// The starting path.
 | 
			
		||||
	start: {
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
},
 | 
			
		||||
	// The paths in the sketch group.
 | 
			
		||||
	value: [{
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
	type: string,
 | 
			
		||||
} |
 | 
			
		||||
{
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
	type: string,
 | 
			
		||||
	// The x coordinate.
 | 
			
		||||
	x: number,
 | 
			
		||||
} |
 | 
			
		||||
{
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
	type: string,
 | 
			
		||||
	// The x coordinate.
 | 
			
		||||
	x: number,
 | 
			
		||||
	// The y coordinate.
 | 
			
		||||
	y: number,
 | 
			
		||||
} |
 | 
			
		||||
{
 | 
			
		||||
	// The from point.
 | 
			
		||||
	from: [number, number],
 | 
			
		||||
	// The name of the path.
 | 
			
		||||
	name: string,
 | 
			
		||||
	// The to point.
 | 
			
		||||
	to: [number, number],
 | 
			
		||||
	type: string,
 | 
			
		||||
}],
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### lastSegX
 | 
			
		||||
 | 
			
		||||
Returns the last segment of x.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								src/wasm-lib/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								src/wasm-lib/Cargo.lock
									
									
									
										generated
									
									
									
								
							@ -1390,7 +1390,7 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "kcl-lib"
 | 
			
		||||
version = "0.1.34"
 | 
			
		||||
version = "0.1.35"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "anyhow",
 | 
			
		||||
 "async-recursion",
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "kcl-lib"
 | 
			
		||||
description = "KittyCAD Language"
 | 
			
		||||
version = "0.1.34"
 | 
			
		||||
version = "0.1.35"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
license = "MIT"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -66,6 +66,7 @@ impl StdLib {
 | 
			
		||||
            Box::new(crate::std::sketch::TangentialArc),
 | 
			
		||||
            Box::new(crate::std::sketch::TangentialArcTo),
 | 
			
		||||
            Box::new(crate::std::sketch::BezierCurve),
 | 
			
		||||
            Box::new(crate::std::sketch::Hole),
 | 
			
		||||
            Box::new(crate::std::math::Cos),
 | 
			
		||||
            Box::new(crate::std::math::Sin),
 | 
			
		||||
            Box::new(crate::std::math::Tan),
 | 
			
		||||
@ -230,6 +231,42 @@ impl Args {
 | 
			
		||||
        Ok((segment_name, sketch_group))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_sketch_groups(&self) -> Result<(Box<SketchGroup>, Box<SketchGroup>), KclError> {
 | 
			
		||||
        let first_value = self.args.first().ok_or_else(|| {
 | 
			
		||||
            KclError::Type(KclErrorDetails {
 | 
			
		||||
                message: format!("Expected a SketchGroup as the first argument, found `{:?}`", self.args),
 | 
			
		||||
                source_ranges: vec![self.source_range],
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        let sketch_group = if let MemoryItem::SketchGroup(sg) = first_value {
 | 
			
		||||
            sg.clone()
 | 
			
		||||
        } else {
 | 
			
		||||
            return Err(KclError::Type(KclErrorDetails {
 | 
			
		||||
                message: format!("Expected a SketchGroup as the first argument, found `{:?}`", self.args),
 | 
			
		||||
                source_ranges: vec![self.source_range],
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let second_value = self.args.get(1).ok_or_else(|| {
 | 
			
		||||
            KclError::Type(KclErrorDetails {
 | 
			
		||||
                message: format!("Expected a SketchGroup as the second argument, found `{:?}`", self.args),
 | 
			
		||||
                source_ranges: vec![self.source_range],
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        let second_sketch_group = if let MemoryItem::SketchGroup(sg) = second_value {
 | 
			
		||||
            sg.clone()
 | 
			
		||||
        } else {
 | 
			
		||||
            return Err(KclError::Type(KclErrorDetails {
 | 
			
		||||
                message: format!("Expected a SketchGroup as the second argument, found `{:?}`", self.args),
 | 
			
		||||
                source_ranges: vec![self.source_range],
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Ok((sketch_group, second_sketch_group))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_sketch_group(&self) -> Result<Box<SketchGroup>, KclError> {
 | 
			
		||||
        let first_value = self.args.first().ok_or_else(|| {
 | 
			
		||||
            KclError::Type(KclErrorDetails {
 | 
			
		||||
 | 
			
		||||
@ -1395,6 +1395,50 @@ async fn inner_bezier_curve(
 | 
			
		||||
    Ok(new_sketch_group)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Use a sketch to cut a hole in another sketch.
 | 
			
		||||
pub async fn hole(args: Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    let (hole_sketch_group, sketch_group): (Box<SketchGroup>, Box<SketchGroup>) = args.get_sketch_groups()?;
 | 
			
		||||
 | 
			
		||||
    let new_sketch_group = inner_hole(hole_sketch_group, sketch_group, args).await?;
 | 
			
		||||
    Ok(MemoryItem::SketchGroup(new_sketch_group))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Use a sketch to cut a hole in another sketch.
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "hole",
 | 
			
		||||
}]
 | 
			
		||||
async fn inner_hole(
 | 
			
		||||
    hole_sketch_group: Box<SketchGroup>,
 | 
			
		||||
    sketch_group: Box<SketchGroup>,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<Box<SketchGroup>, KclError> {
 | 
			
		||||
    //TODO: batch these (once we have batch)
 | 
			
		||||
 | 
			
		||||
    args.send_modeling_cmd(
 | 
			
		||||
        uuid::Uuid::new_v4(),
 | 
			
		||||
        ModelingCmd::Solid2DAddHole {
 | 
			
		||||
            object_id: sketch_group.id,
 | 
			
		||||
            hole_id: hole_sketch_group.id,
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
    .await?;
 | 
			
		||||
 | 
			
		||||
    //suggestion (mike)
 | 
			
		||||
    //we also hide the source hole since its essentially "consumed" by this operation
 | 
			
		||||
    args.send_modeling_cmd(
 | 
			
		||||
        uuid::Uuid::new_v4(),
 | 
			
		||||
        ModelingCmd::ObjectVisible {
 | 
			
		||||
            object_id: hole_sketch_group.id,
 | 
			
		||||
            hidden: true,
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
    .await?;
 | 
			
		||||
 | 
			
		||||
    // TODO: should we modify the sketch group to include the hole data, probably?
 | 
			
		||||
 | 
			
		||||
    Ok(sketch_group)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -431,3 +431,76 @@ const part004 = startSketchOn('YZ')
 | 
			
		||||
    let result = execute_and_snapshot(code).await.unwrap();
 | 
			
		||||
    twenty_twenty::assert_image("tests/executor/outputs/lots_of_planes.png", &result, 0.999);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
async fn serial_test_holes() {
 | 
			
		||||
    let code = r#"fn circle = (pos, radius) => {
 | 
			
		||||
    const sg = startSketchOn('XY')
 | 
			
		||||
      |> startProfileAt(pos, %)
 | 
			
		||||
      |> arc({angle_end: 360, angle_start: 0, radius: radius}, %)
 | 
			
		||||
      |> close(%)
 | 
			
		||||
 | 
			
		||||
    return sg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const square = startSketchOn('XY')
 | 
			
		||||
  |> startProfileAt([0, 0], %)
 | 
			
		||||
  |> line([0, 10], %)
 | 
			
		||||
  |> line([10, 0], %)
 | 
			
		||||
  |> line([0, -10], %)
 | 
			
		||||
  |> close(%)
 | 
			
		||||
  |> hole(circle([2, 2], .5), %)
 | 
			
		||||
  |> hole(circle([2, 8], .5), %)
 | 
			
		||||
  |> extrude(2, %)
 | 
			
		||||
 | 
			
		||||
show(square)
 | 
			
		||||
"#;
 | 
			
		||||
 | 
			
		||||
    let result = execute_and_snapshot(code).await.unwrap();
 | 
			
		||||
    twenty_twenty::assert_image("tests/executor/outputs/holes.png", &result, 0.999);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
async fn serial_test_rounded_with_holes() {
 | 
			
		||||
    let code = r#"fn circle = (pos, radius) => {
 | 
			
		||||
  const sg = startSketchOn('XY')
 | 
			
		||||
    |> startProfileAt([pos[0] + radius, pos[1]], %)
 | 
			
		||||
    |> arc({
 | 
			
		||||
       angle_end: 360,
 | 
			
		||||
       angle_start: 0,
 | 
			
		||||
       radius: radius
 | 
			
		||||
     }, %)
 | 
			
		||||
    |> close(%)
 | 
			
		||||
  return sg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn roundedRectangle = (pos, w, l, cornerRadius) => {
 | 
			
		||||
  const rr = startSketchOn('XY')
 | 
			
		||||
    |> startProfileAt([pos[0] - w/2, 0], %)
 | 
			
		||||
    |> lineTo([pos[0] - w/2, pos[1] - l/2 + cornerRadius], %)
 | 
			
		||||
    |> tangentialArcTo([pos[0] - w/2 + cornerRadius, pos[1] - l/2], %)
 | 
			
		||||
    |> lineTo([pos[0] + w/2 - cornerRadius, pos[1] - l/2], %)
 | 
			
		||||
    |> tangentialArcTo([pos[0] + w/2, pos[1] - l/2 + cornerRadius], %)
 | 
			
		||||
    |> lineTo([pos[0] + w/2, pos[1] + l/2 - cornerRadius], %)
 | 
			
		||||
    |> tangentialArcTo([pos[0] + w/2 - cornerRadius, pos[1] + l/2], %)
 | 
			
		||||
    |> lineTo([pos[0] - w/2 + cornerRadius, pos[1] + l/2], %)
 | 
			
		||||
    |> tangentialArcTo([pos[0] - w/2, pos[1] + l/2 - cornerRadius], %)
 | 
			
		||||
    |> close(%)
 | 
			
		||||
  return rr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const holeRadius = 1
 | 
			
		||||
const holeIndex = 6
 | 
			
		||||
 | 
			
		||||
const part = roundedRectangle([0, 0], 20, 20, 4)
 | 
			
		||||
  |> hole(circle([-holeIndex, holeIndex], holeRadius), %)
 | 
			
		||||
  |> hole(circle([holeIndex, holeIndex], holeRadius), %)
 | 
			
		||||
  |> hole(circle([-holeIndex, -holeIndex], holeRadius), %)
 | 
			
		||||
  |> hole(circle([holeIndex, -holeIndex], holeRadius), %)
 | 
			
		||||
  |> extrude(2, %)
 | 
			
		||||
 | 
			
		||||
show(part)"#;
 | 
			
		||||
 | 
			
		||||
    let result = execute_and_snapshot(code).await.unwrap();
 | 
			
		||||
    twenty_twenty::assert_image("tests/executor/outputs/rounded_with_holes.png", &result, 0.999);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								src/wasm-lib/tests/executor/outputs/holes.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/wasm-lib/tests/executor/outputs/holes.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 70 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/wasm-lib/tests/executor/outputs/rounded_with_holes.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/wasm-lib/tests/executor/outputs/rounded_with_holes.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 77 KiB  | 
		Reference in New Issue
	
	Block a user