Merge branch 'main' into cut-release-v0.25.1
This commit is contained in:
		@ -364,45 +364,48 @@ test.describe('Testing settings', () => {
 | 
			
		||||
    async ({ browser: _ }, testInfo) => {
 | 
			
		||||
      const { electronApp, page } = await setupElectron({
 | 
			
		||||
        testInfo,
 | 
			
		||||
        folderSetupFn: async () => {},
 | 
			
		||||
        folderSetupFn: async (dir) => {
 | 
			
		||||
          const bracketDir = join(dir, 'project-000')
 | 
			
		||||
          await fsp.mkdir(bracketDir, { recursive: true })
 | 
			
		||||
          await fsp.copyFile(
 | 
			
		||||
            executorInputPath('cube.kcl'),
 | 
			
		||||
            join(bracketDir, 'main.kcl')
 | 
			
		||||
          )
 | 
			
		||||
          await fsp.copyFile(
 | 
			
		||||
            executorInputPath('cylinder.kcl'),
 | 
			
		||||
            join(bracketDir, '2.kcl')
 | 
			
		||||
          )
 | 
			
		||||
        },
 | 
			
		||||
      })
 | 
			
		||||
      const kclCube = await fsp.readFile(executorInputPath('cube.kcl'), 'utf-8')
 | 
			
		||||
      const kclCylinder = await fsp.readFile(
 | 
			
		||||
        executorInputPath('cylinder.kcl'),
 | 
			
		||||
        'utf8'
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      const {
 | 
			
		||||
        openKclCodePanel,
 | 
			
		||||
        openFilePanel,
 | 
			
		||||
        createAndSelectProject,
 | 
			
		||||
        pasteCodeInEditor,
 | 
			
		||||
        createNewFileAndSelect,
 | 
			
		||||
        waitForPageLoad,
 | 
			
		||||
        selectFile,
 | 
			
		||||
        editorTextMatches,
 | 
			
		||||
      } = await getUtils(page, test)
 | 
			
		||||
 | 
			
		||||
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
			
		||||
      page.on('console', console.log)
 | 
			
		||||
 | 
			
		||||
      await test.step('Precondition: No projects exist', async () => {
 | 
			
		||||
      await test.step('Precondition: Open to second project file', async () => {
 | 
			
		||||
        await expect(page.getByTestId('home-section')).toBeVisible()
 | 
			
		||||
        const projectLinksPre = page.getByTestId('project-link')
 | 
			
		||||
        await expect(projectLinksPre).toHaveCount(0)
 | 
			
		||||
        await page.getByText('project-000').click()
 | 
			
		||||
        await waitForPageLoad()
 | 
			
		||||
        await openKclCodePanel()
 | 
			
		||||
        await openFilePanel()
 | 
			
		||||
        await editorTextMatches(kclCube)
 | 
			
		||||
 | 
			
		||||
        await selectFile('2.kcl')
 | 
			
		||||
        await editorTextMatches(kclCylinder)
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await createAndSelectProject('project-000')
 | 
			
		||||
 | 
			
		||||
      await openKclCodePanel()
 | 
			
		||||
      const kclCube = await fsp.readFile(
 | 
			
		||||
        'src/wasm-lib/tests/executor/inputs/cube.kcl',
 | 
			
		||||
        'utf-8'
 | 
			
		||||
      )
 | 
			
		||||
      await pasteCodeInEditor(kclCube)
 | 
			
		||||
 | 
			
		||||
      await openFilePanel()
 | 
			
		||||
      await createNewFileAndSelect('2.kcl')
 | 
			
		||||
 | 
			
		||||
      const kclCylinder = await fsp.readFile(
 | 
			
		||||
        'src/wasm-lib/tests/executor/inputs/cylinder.kcl',
 | 
			
		||||
        'utf-8'
 | 
			
		||||
      )
 | 
			
		||||
      await pasteCodeInEditor(kclCylinder)
 | 
			
		||||
 | 
			
		||||
      const settingsOpenButton = page.getByRole('link', {
 | 
			
		||||
        name: 'settings Settings',
 | 
			
		||||
      })
 | 
			
		||||
@ -410,6 +413,9 @@ test.describe('Testing settings', () => {
 | 
			
		||||
 | 
			
		||||
      await test.step('Open and close settings', async () => {
 | 
			
		||||
        await settingsOpenButton.click()
 | 
			
		||||
        await expect(
 | 
			
		||||
          page.getByRole('heading', { name: 'Settings', exact: true })
 | 
			
		||||
        ).toBeVisible()
 | 
			
		||||
        await settingsCloseButton.click()
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
 | 
			
		||||
use kcl_lib::test_server;
 | 
			
		||||
use kcl_lib::{settings::types::UnitLength::Mm, test_server};
 | 
			
		||||
use tokio::runtime::Runtime;
 | 
			
		||||
 | 
			
		||||
pub fn bench_execute(c: &mut Criterion) {
 | 
			
		||||
@ -13,26 +13,42 @@ pub fn bench_execute(c: &mut Criterion) {
 | 
			
		||||
        // Configure Criterion.rs to detect smaller differences and increase sample size to improve
 | 
			
		||||
        // precision and counteract the resulting noise.
 | 
			
		||||
        group.sample_size(10);
 | 
			
		||||
        group.bench_with_input(BenchmarkId::new("execute_", name), &code, |b, &s| {
 | 
			
		||||
        group.bench_with_input(BenchmarkId::new("execute", name), &code, |b, &s| {
 | 
			
		||||
            let rt = Runtime::new().unwrap();
 | 
			
		||||
 | 
			
		||||
            // Spawn a future onto the runtime
 | 
			
		||||
            b.iter(|| {
 | 
			
		||||
                rt.block_on(test_server::execute_and_snapshot(
 | 
			
		||||
                    s,
 | 
			
		||||
                    kcl_lib::settings::types::UnitLength::Mm,
 | 
			
		||||
                ))
 | 
			
		||||
                .unwrap();
 | 
			
		||||
                rt.block_on(test_server::execute_and_snapshot(s, Mm)).unwrap();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        group.finish();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
criterion_group!(benches, bench_execute);
 | 
			
		||||
pub fn bench_lego(c: &mut Criterion) {
 | 
			
		||||
    let mut group = c.benchmark_group("executor_lego_pattern");
 | 
			
		||||
    // Configure Criterion.rs to detect smaller differences and increase sample size to improve
 | 
			
		||||
    // precision and counteract the resulting noise.
 | 
			
		||||
    group.sample_size(10);
 | 
			
		||||
    // Create lego bricks with N x 10 bumps, where N is each element of `sizes`.
 | 
			
		||||
    let sizes = vec![1, 2, 4];
 | 
			
		||||
    for size in sizes {
 | 
			
		||||
        group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
 | 
			
		||||
            let rt = Runtime::new().unwrap();
 | 
			
		||||
            let code = LEGO_PROGRAM.replace("{{N}}", &size.to_string());
 | 
			
		||||
            // Spawn a future onto the runtime
 | 
			
		||||
            b.iter(|| {
 | 
			
		||||
                rt.block_on(test_server::execute_and_snapshot(&code, Mm)).unwrap();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    group.finish();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
criterion_group!(benches, bench_lego, bench_execute);
 | 
			
		||||
criterion_main!(benches);
 | 
			
		||||
 | 
			
		||||
const KITT_PROGRAM: &str = include_str!("../../tests/executor/inputs/kittycad_svg.kcl");
 | 
			
		||||
const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl");
 | 
			
		||||
const SERVER_RACK_HEAVY_PROGRAM: &str = include_str!("../../tests/executor/inputs/server-rack-heavy.kcl");
 | 
			
		||||
const SERVER_RACK_LITE_PROGRAM: &str = include_str!("../../tests/executor/inputs/server-rack-lite.kcl");
 | 
			
		||||
const LEGO_PROGRAM: &str = include_str!("../../tests/executor/inputs/slow_lego.kcl.tmpl");
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,10 @@
 | 
			
		||||
//! Functions related to extruding.
 | 
			
		||||
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use derive_docs::stdlib;
 | 
			
		||||
use kittycad::types::ExtrusionFaceCapType;
 | 
			
		||||
use kittycad::types::{ExtrusionFaceCapType, ExtrusionFaceInfo};
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
 | 
			
		||||
@ -99,7 +101,7 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
        args.send_modeling_cmd(
 | 
			
		||||
        args.batch_modeling_cmd(
 | 
			
		||||
            id,
 | 
			
		||||
            kittycad::types::ModelingCmd::Extrude {
 | 
			
		||||
                target: sketch_group.id,
 | 
			
		||||
@ -112,7 +114,7 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args
 | 
			
		||||
        // Disable the sketch mode.
 | 
			
		||||
        args.batch_modeling_cmd(uuid::Uuid::new_v4(), kittycad::types::ModelingCmd::SketchModeDisable {})
 | 
			
		||||
            .await?;
 | 
			
		||||
        extrude_groups.push(do_post_extrude(sketch_group.clone(), length, id, args.clone()).await?);
 | 
			
		||||
        extrude_groups.push(do_post_extrude(sketch_group.clone(), length, args.clone()).await?);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(extrude_groups.into())
 | 
			
		||||
@ -121,7 +123,6 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args
 | 
			
		||||
pub(crate) async fn do_post_extrude(
 | 
			
		||||
    sketch_group: SketchGroup,
 | 
			
		||||
    length: f64,
 | 
			
		||||
    id: Uuid,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<Box<ExtrudeGroup>, KclError> {
 | 
			
		||||
    // Bring the object to the front of the scene.
 | 
			
		||||
@ -165,7 +166,7 @@ pub(crate) async fn do_post_extrude(
 | 
			
		||||
 | 
			
		||||
    let solid3d_info = args
 | 
			
		||||
        .send_modeling_cmd(
 | 
			
		||||
            id,
 | 
			
		||||
            uuid::Uuid::new_v4(),
 | 
			
		||||
            kittycad::types::ModelingCmd::Solid3DGetExtrusionFaceInfo {
 | 
			
		||||
                edge_id,
 | 
			
		||||
                object_id: sketch_group.id,
 | 
			
		||||
@ -218,26 +219,11 @@ pub(crate) async fn do_post_extrude(
 | 
			
		||||
        .await?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Create a hashmap for quick id lookup
 | 
			
		||||
    let mut face_id_map = std::collections::HashMap::new();
 | 
			
		||||
    // creating fake ids for start and end caps is to make extrudes mock-execute safe
 | 
			
		||||
    let (mut start_cap_id, mut end_cap_id) = if args.ctx.is_mock {
 | 
			
		||||
        (Some(Uuid::new_v4()), Some(Uuid::new_v4()))
 | 
			
		||||
    } else {
 | 
			
		||||
        (None, None)
 | 
			
		||||
    };
 | 
			
		||||
    for face_info in face_infos {
 | 
			
		||||
        match face_info.cap {
 | 
			
		||||
            ExtrusionFaceCapType::Bottom => start_cap_id = face_info.face_id,
 | 
			
		||||
            ExtrusionFaceCapType::Top => end_cap_id = face_info.face_id,
 | 
			
		||||
            ExtrusionFaceCapType::None => {
 | 
			
		||||
                if let Some(curve_id) = face_info.curve_id {
 | 
			
		||||
                    face_id_map.insert(curve_id, face_info.face_id);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let Faces {
 | 
			
		||||
        sides: face_id_map,
 | 
			
		||||
        start_cap_id,
 | 
			
		||||
        end_cap_id,
 | 
			
		||||
    } = analyze_faces(&args, face_infos);
 | 
			
		||||
    // Iterate over the sketch_group.value array and add face_id to GeoMeta
 | 
			
		||||
    let new_value = sketch_group
 | 
			
		||||
        .value
 | 
			
		||||
@ -301,3 +287,37 @@ pub(crate) async fn do_post_extrude(
 | 
			
		||||
        edge_cuts: vec![],
 | 
			
		||||
    }))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Default)]
 | 
			
		||||
struct Faces {
 | 
			
		||||
    /// Maps curve ID to face ID for each side.
 | 
			
		||||
    sides: HashMap<Uuid, Option<Uuid>>,
 | 
			
		||||
    /// Top face ID.
 | 
			
		||||
    end_cap_id: Option<Uuid>,
 | 
			
		||||
    /// Bottom face ID.
 | 
			
		||||
    start_cap_id: Option<Uuid>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn analyze_faces(args: &Args, face_infos: Vec<ExtrusionFaceInfo>) -> Faces {
 | 
			
		||||
    let mut faces = Faces {
 | 
			
		||||
        sides: HashMap::with_capacity(face_infos.len()),
 | 
			
		||||
        ..Default::default()
 | 
			
		||||
    };
 | 
			
		||||
    if args.ctx.is_mock {
 | 
			
		||||
        // Create fake IDs for start and end caps, to make extrudes mock-execute safe
 | 
			
		||||
        faces.start_cap_id = Some(Uuid::new_v4());
 | 
			
		||||
        faces.end_cap_id = Some(Uuid::new_v4());
 | 
			
		||||
    }
 | 
			
		||||
    for face_info in face_infos {
 | 
			
		||||
        match face_info.cap {
 | 
			
		||||
            ExtrusionFaceCapType::Bottom => faces.start_cap_id = face_info.face_id,
 | 
			
		||||
            ExtrusionFaceCapType::Top => faces.end_cap_id = face_info.face_id,
 | 
			
		||||
            ExtrusionFaceCapType::None => {
 | 
			
		||||
                if let Some(curve_id) = face_info.curve_id {
 | 
			
		||||
                    faces.sides.insert(curve_id, face_info.face_id);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    faces
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -170,5 +170,5 @@ async fn inner_loft(
 | 
			
		||||
    .await?;
 | 
			
		||||
 | 
			
		||||
    // Using the first sketch as the base curve, idk we might want to change this later.
 | 
			
		||||
    do_post_extrude(sketch_groups[0].clone(), 0.0, id, args).await
 | 
			
		||||
    do_post_extrude(sketch_groups[0].clone(), 0.0, args).await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -299,7 +299,7 @@ async fn inner_revolve(
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    do_post_extrude(sketch_group, 0.0, id, args).await
 | 
			
		||||
    do_post_extrude(sketch_group, 0.0, args).await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										81
									
								
								src/wasm-lib/tests/executor/inputs/slow_lego.kcl.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/wasm-lib/tests/executor/inputs/slow_lego.kcl.tmpl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,81 @@
 | 
			
		||||
// 2x8 Lego Brick
 | 
			
		||||
// A standard Lego brick with 2 bumps wide and 8 bumps long.
 | 
			
		||||
// Define constants
 | 
			
		||||
const lbumps = 10 // number of bumps long
 | 
			
		||||
const wbumps = {{N}} // number of bumps wide
 | 
			
		||||
const pitch = 8.0
 | 
			
		||||
const clearance = 0.1
 | 
			
		||||
const bumpDiam = 4.8
 | 
			
		||||
const bumpHeight = 1.8
 | 
			
		||||
const height = 9.6
 | 
			
		||||
const t = (pitch - (2 * clearance) - bumpDiam) / 2.0
 | 
			
		||||
const totalLength = lbumps * pitch - (2.0 * clearance)
 | 
			
		||||
const totalWidth = wbumps * pitch - (2.0 * clearance)
 | 
			
		||||
// Create the plane for the pegs. This is a hack so that the pegs can be patterned along the face of the lego base.
 | 
			
		||||
const pegFace = {
 | 
			
		||||
  plane: {
 | 
			
		||||
    origin: { x: 0, y: 0, z: height },
 | 
			
		||||
    xAxis: { x: 1, y: 0, z: 0 },
 | 
			
		||||
    yAxis: { x: 0, y: 1, z: 0 },
 | 
			
		||||
    zAxis: { x: 0, y: 0, z: 1 }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// Create the plane for the tubes underneath the lego. This is a hack so that the tubes can be patterned underneath the lego.
 | 
			
		||||
const tubeFace = {
 | 
			
		||||
    plane: {
 | 
			
		||||
    origin: { x: 0, y: 0, z: height - t },
 | 
			
		||||
    xAxis: { x: 1, y: 0, z: 0 },
 | 
			
		||||
    yAxis: { x: 0, y: 1, z: 0 },
 | 
			
		||||
    zAxis: { x: 0, y: 0, z: 1 }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// Make the base
 | 
			
		||||
const s = startSketchOn('XY')
 | 
			
		||||
  |> startProfileAt([-totalWidth / 2, -totalLength / 2], %)
 | 
			
		||||
  |> line([totalWidth, 0], %)
 | 
			
		||||
  |> line([0, totalLength], %)
 | 
			
		||||
  |> line([-totalWidth, 0], %)
 | 
			
		||||
  |> close(%)
 | 
			
		||||
  |> extrude(height, %)
 | 
			
		||||
 | 
			
		||||
// Sketch and extrude a rectangular shape to create the shell underneath the lego. This is a hack until we have a shell function.
 | 
			
		||||
const shellExtrude = startSketchOn(s, "start")
 | 
			
		||||
  |> startProfileAt([
 | 
			
		||||
       -(totalWidth / 2 - t),
 | 
			
		||||
       -(totalLength / 2 - t)
 | 
			
		||||
     ], %)
 | 
			
		||||
  |> line([totalWidth - (2 * t), 0], %)
 | 
			
		||||
  |> line([0, totalLength - (2 * t)], %)
 | 
			
		||||
  |> line([-(totalWidth - (2 * t)), 0], %)
 | 
			
		||||
  |> close(%)
 | 
			
		||||
  |> extrude(-(height - t), %)
 | 
			
		||||
 | 
			
		||||
fn tr = (i) => {
 | 
			
		||||
  let j = i + 1
 | 
			
		||||
  let x = (j/wbumps) * pitch
 | 
			
		||||
  let y = (j % wbumps) * pitch
 | 
			
		||||
  return {
 | 
			
		||||
    translate: [x, y, 0],
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create the pegs on the top of the base
 | 
			
		||||
const totalBumps = (wbumps * lbumps)-1
 | 
			
		||||
const peg = startSketchOn(s, 'end')
 | 
			
		||||
  |> circle([
 | 
			
		||||
       -(pitch*(wbumps-1)/2),
 | 
			
		||||
       -(pitch*(lbumps-1)/2)
 | 
			
		||||
     ], bumpDiam / 2, %)
 | 
			
		||||
  |> patternLinear2d({
 | 
			
		||||
       axis: [1, 0],
 | 
			
		||||
       repetitions: wbumps-1,
 | 
			
		||||
       distance: pitch
 | 
			
		||||
     }, %)
 | 
			
		||||
  |> patternLinear2d({
 | 
			
		||||
       axis: [0, 1],
 | 
			
		||||
       repetitions: lbumps-1,
 | 
			
		||||
       distance: pitch
 | 
			
		||||
     }, %)
 | 
			
		||||
  |> extrude(bumpHeight, %)
 | 
			
		||||
  // |> patternTransform(int(totalBumps-1), tr, %)
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user