Sketch on face (#1371)

* add extra metadata to extrudeGroup

* add boilerplate

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* cleanup and generate docs

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* change plane id to entity id

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* generate docs

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* get face id from extrude using segment tag

* cleanup a bit

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* cleanup a bit

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix doc comment

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* get rid of face_id in geo_meta

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* sketch on face test

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* cleanup edge_id

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix value

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix test

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
This commit is contained in:
Jess Frazelle
2024-02-12 18:08:42 -08:00
committed by GitHub
parent 7386ccf1bf
commit fbcb96add5
11 changed files with 6102 additions and 2145 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -55,11 +55,11 @@ show(mySketch001)`
],
position: [0, 0, 0],
rotation: [0, 0, 0, 1],
xAxis: [1, 0, 0],
yAxis: [0, 1, 0],
zAxis: [0, 0, 1],
xAxis: { x: 1, y: 0, z: 0 },
yAxis: { x: 0, y: 1, z: 0 },
zAxis: { x: 0, y: 0, z: 1 },
id: expect.any(String),
planeId: expect.any(String),
entityId: expect.any(String),
__meta: [{ sourceRange: [46, 71] }],
},
])
@ -88,6 +88,11 @@ show(mySketch001)`
height: 2,
position: [0, 0, 0],
rotation: [0, 0, 0, 1],
endCapId: null,
startCapId: null,
xAxis: { x: 1, y: 0, z: 0 },
yAxis: { x: 0, y: 1, z: 0 },
zAxis: { x: 0, y: 0, z: 1 },
__meta: [{ sourceRange: [46, 71] }],
},
])
@ -130,6 +135,11 @@ show(theExtrude, sk2)`
height: 2,
position: [0, 0, 0],
rotation: [0, 0, 0, 1],
endCapId: null,
startCapId: null,
xAxis: { x: 1, y: 0, z: 0 },
yAxis: { x: 0, y: 1, z: 0 },
zAxis: { x: 0, y: 0, z: 1 },
__meta: [{ sourceRange: [38, 63] }],
},
{
@ -139,6 +149,12 @@ show(theExtrude, sk2)`
height: 2,
position: [0, 0, 0],
rotation: [0, 0, 0, 1],
endCapId: null,
startCapId: null,
xAxis: { x: 1, y: 0, z: 0 },
yAxis: { x: 0, y: 1, z: 0 },
zAxis: { x: 0, y: 0, z: 1 },
__meta: [{ sourceRange: [356, 381] }],
},
])

View File

@ -186,11 +186,11 @@ show(mySketch)
],
position: [0, 0, 0],
rotation: [0, 0, 0, 1],
xAxis: [1, 0, 0],
yAxis: [0, 1, 0],
zAxis: [0, 0, 1],
xAxis: { x: 1, y: 0, z: 0 },
yAxis: { x: 0, y: 1, z: 0 },
zAxis: { x: 0, y: 0, z: 1 },
id: expect.any(String),
planeId: expect.any(String),
entityId: expect.any(String),
__meta: [{ sourceRange: [39, 63] }],
})
})

View File

@ -306,7 +306,7 @@ pub fn get_type_string_from_schema(schema: &schemars::schema::Schema) -> Result<
if let Some(format) = &o.format {
if format == "uuid" {
return Ok((Primitive::Uuid.to_string(), false));
} else if format == "double" || format == "uint" {
} else if format == "double" || format == "uint" || format == "int64" {
return Ok((Primitive::Number.to_string(), false));
} else {
anyhow::bail!("unknown format: {}", format);
@ -424,7 +424,7 @@ pub fn get_autocomplete_string_from_schema(schema: &schemars::schema::Schema) ->
if let Some(format) = &o.format {
if format == "uuid" {
return Ok(Primitive::Uuid.to_string());
} else if format == "double" || format == "uint" {
} else if format == "double" || format == "uint" || format == "int64" {
return Ok(Primitive::Number.to_string());
} else {
anyhow::bail!("unknown format: {}", format);

View File

@ -139,6 +139,7 @@ impl ProgramReturn {
pub enum MemoryItem {
UserVal(UserVal),
Plane(Box<Plane>),
Face(Box<Face>),
SketchGroup(Box<SketchGroup>),
SketchGroups {
value: Vec<Box<SketchGroup>>,
@ -230,6 +231,25 @@ pub struct Plane {
pub meta: Vec<Metadata>,
}
/// A face.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub struct Face {
/// The id of the face.
pub id: uuid::Uuid,
/// The tag of the face.
pub value: String,
/// What should the faces X axis be?
pub x_axis: Point3d,
/// What should the faces Y axis be?
pub y_axis: Point3d,
/// The z-axis (normal).
pub z_axis: Point3d,
#[serde(rename = "__meta")]
pub meta: Vec<Metadata>,
}
/// Type for a plane.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, FromStr, Display)]
#[ts(export)]
@ -312,6 +332,7 @@ impl From<MemoryItem> for Vec<SourceRange> {
MemoryItem::ExtrudeTransform(e) => e.meta.iter().map(|m| m.source_range).collect(),
MemoryItem::Function { meta, .. } => meta.iter().map(|m| m.source_range).collect(),
MemoryItem::Plane(p) => p.meta.iter().map(|m| m.source_range).collect(),
MemoryItem::Face(f) => f.meta.iter().map(|m| m.source_range).collect(),
}
}
}
@ -404,13 +425,13 @@ pub struct SketchGroup {
/// The rotation of the sketch group base plane.
pub rotation: Rotation,
/// The x-axis of the sketch group base plane in the 3D space
pub x_axis: Position,
pub x_axis: Point3d,
/// The y-axis of the sketch group base plane in the 3D space
pub y_axis: Position,
pub y_axis: Point3d,
/// The z-axis of the sketch group base plane in the 3D space
pub z_axis: Position,
/// The plane id of the sketch group.
pub plane_id: Option<uuid::Uuid>,
pub z_axis: Point3d,
/// The plane id or face id of the sketch group.
pub entity_id: Option<uuid::Uuid>,
/// Metadata.
#[serde(rename = "__meta")]
pub meta: Vec<Metadata>,
@ -503,6 +524,16 @@ pub struct ExtrudeGroup {
pub position: Position,
/// The rotation of the extrude group.
pub rotation: Rotation,
/// The x-axis of the extrude group base plane in the 3D space
pub x_axis: Point3d,
/// The y-axis of the extrude group base plane in the 3D space
pub y_axis: Point3d,
/// The z-axis of the extrude group base plane in the 3D space
pub z_axis: Point3d,
/// The id of the extrusion start cap
pub start_cap_id: Option<uuid::Uuid>,
/// The id of the extrusion end cap
pub end_cap_id: Option<uuid::Uuid>,
/// Metadata.
#[serde(rename = "__meta")]
pub meta: Vec<Metadata>,
@ -531,6 +562,16 @@ pub enum BodyType {
#[ts(export)]
pub struct Position(#[ts(type = "[number, number, number]")] pub [f64; 3]);
impl From<Position> for Point3d {
fn from(p: Position) -> Self {
Self {
x: p.0[0],
y: p.0[1],
z: p.0[2],
}
}
}
#[derive(Debug, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS, JsonSchema)]
#[ts(export)]
pub struct Rotation(#[ts(type = "[number, number, number, number]")] pub [f64; 4]);
@ -773,6 +814,16 @@ impl Path {
Path::TangentialArcTo { base, .. } => base,
}
}
pub fn get_base_mut(&mut self) -> Option<&mut BasePath> {
match self {
Path::ToPoint { base } => Some(base),
Path::Horizontal { base, .. } => Some(base),
Path::AngledLineTo { base, .. } => Some(base),
Path::Base { base } => Some(base),
Path::TangentialArcTo { base, .. } => Some(base),
}
}
}
/// An extrude surface.
@ -781,41 +832,49 @@ impl Path {
#[serde(tag = "type", rename_all = "camelCase")]
pub enum ExtrudeSurface {
/// An extrude plane.
ExtrudePlane {
ExtrudePlane(ExtrudePlane),
}
/// An extruded plane.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub struct ExtrudePlane {
/// The position.
position: Position,
pub position: Position,
/// The rotation.
rotation: Rotation,
pub rotation: Rotation,
/// The face id for the extrude plane.
pub face_id: uuid::Uuid,
/// The name.
name: String,
pub name: String,
/// Metadata.
#[serde(flatten)]
geo_meta: GeoMeta,
},
pub geo_meta: GeoMeta,
}
impl ExtrudeSurface {
pub fn get_id(&self) -> uuid::Uuid {
match self {
ExtrudeSurface::ExtrudePlane { geo_meta, .. } => geo_meta.id,
ExtrudeSurface::ExtrudePlane(ep) => ep.geo_meta.id,
}
}
pub fn get_name(&self) -> String {
match self {
ExtrudeSurface::ExtrudePlane { name, .. } => name.clone(),
ExtrudeSurface::ExtrudePlane(ep) => ep.name.clone(),
}
}
pub fn get_position(&self) -> Position {
match self {
ExtrudeSurface::ExtrudePlane { position, .. } => *position,
ExtrudeSurface::ExtrudePlane(ep) => ep.position,
}
}
pub fn get_rotation(&self) -> Rotation {
match self {
ExtrudeSurface::ExtrudePlane { rotation, .. } => *rotation,
ExtrudeSurface::ExtrudePlane(ep) => ep.rotation,
}
}
}

View File

@ -6,7 +6,7 @@ use schemars::JsonSchema;
use crate::{
errors::{KclError, KclErrorDetails},
executor::{ExtrudeGroup, ExtrudeTransform, MemoryItem, SketchGroup},
executor::{ExtrudeGroup, ExtrudeSurface, ExtrudeTransform, GeoMeta, MemoryItem, Path, SketchGroup},
std::Args,
};
@ -46,17 +46,93 @@ async fn inner_extrude(length: f64, sketch_group: Box<SketchGroup>, args: Args)
)
.await?;
if sketch_group.value.is_empty() {
return Err(KclError::Type(KclErrorDetails {
message: "Expected a non-empty sketch group".to_string(),
source_ranges: vec![args.source_range],
}));
}
let mut edge_id = None;
for segment in sketch_group.value.iter() {
if let Path::ToPoint { base } = segment {
edge_id = Some(base.geo_meta.id);
break;
}
}
let Some(edge_id) = edge_id else {
return Err(KclError::Type(KclErrorDetails {
message: "Expected a Path::ToPoint variant".to_string(),
source_ranges: vec![args.source_range],
}));
};
let solid3d_info = args
.send_modeling_cmd(
id,
kittycad::types::ModelingCmd::Solid3DGetExtrusionFaceInfo {
edge_id,
object_id: sketch_group.id,
},
)
.await?;
let face_infos = if let kittycad::types::OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::Solid3DGetExtrusionFaceInfo { data },
} = solid3d_info
{
data.faces
} else {
vec![]
};
// Create a hashmap for quick id lookup
let mut face_id_map = std::collections::HashMap::new();
let mut start_cap_id = None;
let mut end_cap_id = None;
for face_info in face_infos {
match face_info.cap {
kittycad::types::ExtrusionFaceCapType::Bottom => start_cap_id = face_info.face_id,
kittycad::types::ExtrusionFaceCapType::Top => end_cap_id = face_info.face_id,
_ => {
if let Some(curve_id) = face_info.curve_id {
face_id_map.insert(curve_id, face_info.face_id);
}
}
}
}
// Iterate over the sketch_group.value array and add face_id to GeoMeta
let mut new_value: Vec<ExtrudeSurface> = Vec::new();
for path in sketch_group.value.iter() {
if let Some(Some(actual_face_id)) = face_id_map.get(&path.get_base().geo_meta.id) {
let extrude_surface = ExtrudeSurface::ExtrudePlane(crate::executor::ExtrudePlane {
position: sketch_group.position, // TODO should be for the extrude surface
rotation: sketch_group.rotation, // TODO should be for the extrude surface
face_id: *actual_face_id,
name: path.get_base().name.clone(),
geo_meta: GeoMeta {
id: path.get_base().geo_meta.id,
metadata: path.get_base().geo_meta.metadata.clone(),
},
});
new_value.push(extrude_surface);
}
}
Ok(Box::new(ExtrudeGroup {
// Ok so you would think that the id would be the id of the extrude group,
// that we passed in to the function, but it's actually the id of the
// sketch group.
id: sketch_group.id,
// TODO, this is just an empty array now, should be deleted. This
// comment was originally in the JS code.
value: Default::default(),
id,
value: new_value,
height: length,
position: sketch_group.position,
rotation: sketch_group.rotation,
x_axis: sketch_group.x_axis,
y_axis: sketch_group.y_axis,
z_axis: sketch_group.z_axis,
start_cap_id,
end_cap_id,
meta: sketch_group.meta,
}))
}

View File

@ -22,13 +22,14 @@ use serde::{Deserialize, Serialize};
use self::kcl_stdlib::KclStdLibFn;
use crate::{
ast::types::parse_json_number_as_f64,
ast::types::{parse_json_number_as_f64, parse_json_value_as_string},
docs::StdLibFn,
engine::EngineManager,
errors::{KclError, KclErrorDetails},
executor::{
ExecutorContext, ExtrudeGroup, Geometry, MemoryItem, Metadata, Plane, SketchGroup, SketchGroupSet, SourceRange,
ExecutorContext, ExtrudeGroup, Geometry, MemoryItem, Metadata, SketchGroup, SketchGroupSet, SourceRange,
},
std::sketch::SketchSurface,
};
pub type StdFn = fn(Args) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<MemoryItem, KclError>>>>;
@ -384,7 +385,6 @@ impl Args {
})
})?
.get_json_value()?;
let data: String = serde_json::from_value(first_value).map_err(|e| {
KclError::Type(KclErrorDetails {
message: format!("Expected a file path string: {}", e),
@ -406,6 +406,33 @@ impl Args {
}
}
fn get_data_and_optional_tag<T: serde::de::DeserializeOwned>(&self) -> Result<(T, Option<String>), KclError> {
let first_value = self
.args
.first()
.ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Expected a struct as the first argument, found `{:?}`", self.args),
source_ranges: vec![self.source_range],
})
})?
.get_json_value()?;
let data: T = serde_json::from_value(first_value).map_err(|e| {
KclError::Type(KclErrorDetails {
message: format!("Failed to deserialize struct from JSON: {}", e),
source_ranges: vec![self.source_range],
})
})?;
if let Some(second_value) = self.args.get(1) {
let tag = parse_json_value_as_string(&second_value.get_json_value()?);
Ok((data, tag))
} else {
Ok((data, None))
}
}
fn get_data_and_sketch_group<T: serde::de::DeserializeOwned>(&self) -> Result<(T, Box<SketchGroup>), KclError> {
let first_value = self
.args
@ -487,7 +514,7 @@ impl Args {
Ok((data, geometry))
}
fn get_data_and_plane<T: serde::de::DeserializeOwned>(&self) -> Result<(T, Box<Plane>), KclError> {
fn get_data_and_sketch_surface<T: serde::de::DeserializeOwned>(&self) -> Result<(T, SketchSurface), KclError> {
let first_value = self
.args
.first()
@ -513,16 +540,21 @@ impl Args {
})
})?;
let plane = if let MemoryItem::Plane(p) = second_value {
p.clone()
let sketch_surface = if let MemoryItem::Plane(p) = second_value {
SketchSurface::Plane(p.clone())
} else if let MemoryItem::Face(face) = second_value {
SketchSurface::Face(face.clone())
} else {
return Err(KclError::Type(KclErrorDetails {
message: format!("Expected a Plane as the second argument, found `{:?}`", self.args),
message: format!(
"Expected a plane or face (SketchSurface) as the second argument, found `{:?}`",
self.args
),
source_ranges: vec![self.source_range],
}));
};
Ok((data, plane))
Ok((data, sketch_surface))
}
fn get_segment_name_to_number_sketch_group(&self) -> Result<(String, f64, Box<SketchGroup>), KclError> {

View File

@ -10,8 +10,8 @@ use serde::{Deserialize, Serialize};
use crate::{
errors::{KclError, KclErrorDetails},
executor::{
BasePath, GeoMeta, MemoryItem, Path, Plane, PlaneType, Point2d, Point3d, Position, Rotation, SketchGroup,
SketchGroupSet, SourceRange,
BasePath, ExtrudeGroup, ExtrudeSurface, Face, GeoMeta, MemoryItem, Path, Plane, PlaneType, Point2d, Point3d,
Position, Rotation, SketchGroup, SketchGroupSet, SourceRange,
},
std::{
utils::{
@ -646,11 +646,21 @@ pub async fn start_sketch_at(args: Args) -> Result<MemoryItem, KclError> {
async fn inner_start_sketch_at(data: LineData, args: Args) -> Result<Box<SketchGroup>, KclError> {
// Let's assume it's the XY plane for now, this is just for backwards compatibility.
let xy_plane = PlaneData::XY;
let plane = inner_start_sketch_on(xy_plane, args.clone()).await?;
let sketch_group = inner_start_profile_at(data, plane, args).await?;
let sketch_surface = inner_start_sketch_on(SketchData::Plane(xy_plane), None, args.clone()).await?;
let sketch_group = inner_start_profile_at(data, sketch_surface, args).await?;
Ok(sketch_group)
}
/// Data for start sketch on.
/// You can start a sketch on a plane or an extrude group.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "camelCase", untagged)]
pub enum SketchData {
Plane(PlaneData),
ExtrudeGroup(Box<ExtrudeGroup>),
}
/// Data for a plane.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, ExecutionPlanValue)]
#[ts(export)]
@ -763,19 +773,116 @@ impl From<PlaneData> for Plane {
}
}
/// Start a sketch on a specific plane.
pub async fn start_sketch_on(args: Args) -> Result<MemoryItem, KclError> {
let data: PlaneData = args.get_data()?;
let plane = inner_start_sketch_on(data, args).await?;
Ok(MemoryItem::Plane(plane))
/// A plane or a face.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(rename_all = "camelCase", untagged)]
pub enum SketchSurface {
/// A plane.
Plane(Box<Plane>),
/// A face.
Face(Box<Face>),
}
/// Start a sketch at a given point.
impl SketchSurface {
pub fn id(&self) -> uuid::Uuid {
match self {
SketchSurface::Plane(plane) => plane.id,
SketchSurface::Face(face) => face.id,
}
}
pub fn x_axis(&self) -> Point3d {
match self {
SketchSurface::Plane(plane) => plane.x_axis.clone(),
SketchSurface::Face(face) => face.x_axis.clone(),
}
}
pub fn y_axis(&self) -> Point3d {
match self {
SketchSurface::Plane(plane) => plane.y_axis.clone(),
SketchSurface::Face(face) => face.y_axis.clone(),
}
}
pub fn z_axis(&self) -> Point3d {
match self {
SketchSurface::Plane(plane) => plane.z_axis.clone(),
SketchSurface::Face(face) => face.z_axis.clone(),
}
}
}
/// Start a sketch on a specific plane or face.
pub async fn start_sketch_on(args: Args) -> Result<MemoryItem, KclError> {
let (data, tag): (SketchData, Option<String>) = args.get_data_and_optional_tag()?;
match inner_start_sketch_on(data, tag, args).await? {
SketchSurface::Plane(plane) => Ok(MemoryItem::Plane(plane)),
SketchSurface::Face(face) => Ok(MemoryItem::Face(face)),
}
}
/// Start a sketch on a specific plane or face.
#[stdlib {
name = "startSketchOn",
}]
async fn inner_start_sketch_on(data: PlaneData, args: Args) -> Result<Box<Plane>, KclError> {
async fn inner_start_sketch_on(data: SketchData, tag: Option<String>, args: Args) -> Result<SketchSurface, KclError> {
match data {
SketchData::Plane(plane_data) => {
let plane = start_sketch_on_plane(plane_data, args).await?;
Ok(SketchSurface::Plane(plane))
}
SketchData::ExtrudeGroup(extrude_group) => {
let Some(tag) = tag else {
return Err(KclError::Type(KclErrorDetails {
message: "Expected a tag for the face to sketch on".to_string(),
source_ranges: vec![args.source_range],
}));
};
let face = start_sketch_on_face(extrude_group, &tag, args).await?;
Ok(SketchSurface::Face(face))
}
}
}
async fn start_sketch_on_face(extrude_group: Box<ExtrudeGroup>, tag: &str, args: Args) -> Result<Box<Face>, KclError> {
let extrude_plane = extrude_group
.value
.iter()
.find_map(|extrude_surface| match extrude_surface {
ExtrudeSurface::ExtrudePlane(extrude_plane) if extrude_plane.name == tag => Some(extrude_plane),
ExtrudeSurface::ExtrudePlane(_) => None,
})
.ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Expected a face with the tag `{}`", tag),
source_ranges: vec![args.source_range],
})
})?;
// Enter sketch mode on the face.
let id = uuid::Uuid::new_v4();
args.send_modeling_cmd(
id,
ModelingCmd::EnableSketchMode {
animated: false,
ortho: false,
entity_id: extrude_plane.face_id,
},
)
.await?;
Ok(Box::new(Face {
id,
value: tag.to_string(),
// TODO: get this from the extrude plane data.
x_axis: extrude_group.x_axis,
y_axis: extrude_group.y_axis,
z_axis: extrude_group.z_axis,
meta: vec![args.source_range.into()],
}))
}
async fn start_sketch_on_plane(data: PlaneData, args: Args) -> Result<Box<Plane>, KclError> {
let mut plane: Plane = data.clone().into();
let id = uuid::Uuid::new_v4();
let default_origin = Point3D { x: 0.0, y: 0.0, z: 0.0 };
@ -874,9 +981,9 @@ async fn inner_start_sketch_on(data: PlaneData, args: Args) -> Result<Box<Plane>
/// Start a profile at a given point.
pub async fn start_profile_at(args: Args) -> Result<MemoryItem, KclError> {
let (data, plane): (LineData, Box<Plane>) = args.get_data_and_plane()?;
let (data, sketch_surface): (LineData, SketchSurface) = args.get_data_and_sketch_surface()?;
let sketch_group = inner_start_profile_at(data, plane, args).await?;
let sketch_group = inner_start_profile_at(data, sketch_surface, args).await?;
Ok(MemoryItem::SketchGroup(sketch_group))
}
@ -884,7 +991,11 @@ pub async fn start_profile_at(args: Args) -> Result<MemoryItem, KclError> {
#[stdlib {
name = "startProfileAt",
}]
async fn inner_start_profile_at(data: LineData, plane: Box<Plane>, args: Args) -> Result<Box<SketchGroup>, KclError> {
async fn inner_start_profile_at(
data: LineData,
sketch_surface: SketchSurface,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
let to = match &data {
LineData::PointWithTag { to, .. } => *to,
LineData::Point(to) => *to,
@ -925,10 +1036,10 @@ async fn inner_start_profile_at(data: LineData, plane: Box<Plane>, args: Args) -
id: path_id,
position: Position([0.0, 0.0, 0.0]),
rotation: Rotation([0.0, 0.0, 0.0, 1.0]),
x_axis: Position([plane.x_axis.x, plane.x_axis.y, plane.x_axis.z]),
y_axis: Position([plane.y_axis.x, plane.y_axis.y, plane.y_axis.z]),
z_axis: Position([plane.z_axis.x, plane.z_axis.y, plane.z_axis.z]),
plane_id: Some(plane.id),
x_axis: sketch_surface.x_axis(),
y_axis: sketch_surface.y_axis(),
z_axis: sketch_surface.z_axis(),
entity_id: Some(sketch_surface.id()),
value: vec![],
start: current_path,
meta: vec![args.source_range.into()],
@ -963,8 +1074,8 @@ async fn inner_close(sketch_group: Box<SketchGroup>, args: Args) -> Result<Box<S
)
.await?;
// Exit sketch mode, since if we were in a plane we'd want to disable the sketch mode after.
if sketch_group.plane_id.is_some() {
// Exit sketch mode, since if we were in a plane or entity we'd want to disable the sketch mode after.
if sketch_group.entity_id.is_some() {
// We were on a plane, disable the sketch mode.
args.send_modeling_cmd(uuid::Uuid::new_v4(), ModelingCmd::SketchModeDisable {})
.await?;

View File

@ -23,6 +23,8 @@ async fn execute_and_snapshot(code: &str) -> Result<image::DynamicImage> {
// Create the client.
let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
// uncomment to use a local server
// client.set_base_url("http://your-local-server:8080/");
let ws = client
.modeling()
@ -66,6 +68,29 @@ async fn execute_and_snapshot(code: &str) -> Result<image::DynamicImage> {
Ok(actual)
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_sketch_on_face() {
let code = r#"const part001 = startSketchOn('XY')
|> startProfileAt([11.19, 28.35], %)
|> line({to: [28.67, -13.25], tag: "here"}, %)
|> line([-4.12, -22.81], %)
|> line([-33.24, 14.55], %)
|> close(%)
|> extrude(5, %)
const part002 = startSketchOn(part001, "here")
|> startProfileAt([0, 0], %)
|> line([0, 10], %)
|> line([10, 0], %)
|> line([0, -10], %)
|> close(%)
|> extrude(5, %)
"#;
let result = execute_and_snapshot(code).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face.png", &result, 0.999);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_execute_with_function_sketch() {
let code = r#"fn box = (h, l, w) => {

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB