Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
3dfc2c86e1 | |||
71647ede29 | |||
cd679f4be3 | |||
18d87b99bd | |||
70a2202877 | |||
f5c9f84ae9 | |||
71f701dec7 | |||
bffbed1d42 | |||
9f60ed8e75 |
@ -1353,6 +1353,99 @@ test.describe(`Sketching with offset planes`, () => {
|
||||
})
|
||||
|
||||
test.describe('multi-profile sketching', () => {
|
||||
test(
|
||||
`test it removes half-finished expressions when changing tools in sketch mode`,
|
||||
{ tag: ['@skipWin'] },
|
||||
async ({ context, page, scene, toolbar, editor, homePage }) => {
|
||||
// We seed the scene with a single offset plane
|
||||
await context.addInitScript(() => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
sketch001 = startSketchOn('XZ')
|
||||
profile001 = startProfileAt([121.52, 168.25], sketch001)
|
||||
|> line(end = [115.04, 113.61])
|
||||
|> line(end = [130.87, -97.79])
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
profile002 = startProfileAt([117.2, 56.08], sketch001)
|
||||
|> line(end = [166.82, 25.89])
|
||||
|> yLine(-107.86, %)
|
||||
|
||||
`
|
||||
)
|
||||
})
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
|
||||
await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick()
|
||||
await page.waitForTimeout(600)
|
||||
|
||||
const [circlePoint1] = scene.makeMouseHelpers(700, 200)
|
||||
|
||||
await test.step('equip circle tool and click first point', async () => {
|
||||
await toolbar.circleBtn.click()
|
||||
await page.waitForTimeout(100)
|
||||
await circlePoint1()
|
||||
await editor.expectEditor.toContain('profile003 = circle({ center = [')
|
||||
})
|
||||
|
||||
await test.step('equip line tool and verify circle code is removed', async () => {
|
||||
await toolbar.lineBtn.click()
|
||||
await editor.expectEditor.not.toContain('profile003 = circle({')
|
||||
})
|
||||
|
||||
const [circle3Point1] = scene.makeMouseHelpers(650, 200)
|
||||
const [circle3Point2] = scene.makeMouseHelpers(750, 200)
|
||||
|
||||
await test.step('equip three point circle tool and click first two points', async () => {
|
||||
await toolbar.selectCircleThreePoint()
|
||||
await page.waitForTimeout(100)
|
||||
await circle3Point1()
|
||||
await page.waitForTimeout(100)
|
||||
await circle3Point2()
|
||||
await editor.expectEditor.toContain('profile003 = circleThreePoint(')
|
||||
})
|
||||
|
||||
await test.step('equip line tool and verify three point circle code is removed', async () => {
|
||||
await toolbar.lineBtn.click()
|
||||
await editor.expectEditor.not.toContain(
|
||||
'profile003 = circleThreePoint('
|
||||
)
|
||||
})
|
||||
|
||||
const [cornerRectPoint1] = scene.makeMouseHelpers(600, 300)
|
||||
|
||||
await test.step('equip corner rectangle tool and click first point', async () => {
|
||||
await toolbar.rectangleBtn.click()
|
||||
await page.waitForTimeout(100)
|
||||
await cornerRectPoint1()
|
||||
await editor.expectEditor.toContain('profile003 = startProfileAt(')
|
||||
})
|
||||
|
||||
await test.step('equip line tool and verify corner rectangle code is removed', async () => {
|
||||
await toolbar.lineBtn.click()
|
||||
await editor.expectEditor.not.toContain('profile003 = startProfileAt(')
|
||||
})
|
||||
|
||||
const [centerRectPoint1] = scene.makeMouseHelpers(700, 300)
|
||||
|
||||
await test.step('equip center rectangle tool and click first point', async () => {
|
||||
await toolbar.selectCenterRectangle()
|
||||
await page.waitForTimeout(100)
|
||||
await centerRectPoint1()
|
||||
await editor.expectEditor.toContain('profile003 = startProfileAt(')
|
||||
})
|
||||
|
||||
await test.step('equip line tool and verify center rectangle code is removed', async () => {
|
||||
await toolbar.lineBtn.click()
|
||||
await editor.expectEditor.not.toContain('profile003 = startProfileAt(')
|
||||
})
|
||||
}
|
||||
)
|
||||
test(
|
||||
`snapToProfile start only works for current profile`,
|
||||
{ tag: ['@skipWin'] },
|
||||
@ -1885,7 +1978,7 @@ profile004 = circleThreePoint(sketch001, p1 = [13.44, -6.8], p2 = [13.39, -2.07]
|
||||
)
|
||||
test(
|
||||
'Can delete a profile in the editor while is sketch mode, and sketch mode does not break, can ctrl+z to undo after constraint with variable was added',
|
||||
{ tag: ['@skipWin'] },
|
||||
{ tag: ['@skipWin', '@skipLinux'] },
|
||||
async ({ scene, toolbar, editor, cmdBar, page, homePage }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -1969,7 +2062,7 @@ profile003 = circle({ center = [6.92, -4.2], radius = 3.16 }, sketch001)
|
||||
activeLines: ['|>line(end = [-0.41,6.99])'],
|
||||
highlightedCode: 'line(end = [-0.41,6.99])',
|
||||
})
|
||||
}).toPass({ timeout: 10_000, intervals: [1000] })
|
||||
}).toPass({ timeout: 30_000, intervals: [1500] })
|
||||
|
||||
await toolbar.lengthConstraintBtn.click()
|
||||
await cmdBar.progressCmdBar()
|
||||
|
@ -85,7 +85,7 @@
|
||||
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
|
||||
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
|
||||
"fetch:wasm": "./get-latest-wasm-bundle.sh",
|
||||
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/achalmers/offset-plane-kwargs/manifest.json",
|
||||
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/next/manifest.json",
|
||||
"isomorphic-copy-wasm": "(copy src/wasm-lib/pkg/wasm_lib_bg.wasm public || cp src/wasm-lib/pkg/wasm_lib_bg.wasm public)",
|
||||
"build:wasm-dev": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && yarn isomorphic-copy-wasm && yarn fmt",
|
||||
"build:wasm": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",
|
||||
|
@ -1574,7 +1574,7 @@ export const ModelingMachineProvider = ({
|
||||
: updatedSketchNodePaths[0]
|
||||
}
|
||||
|
||||
if (doesNeedSplitting) {
|
||||
if (doesNeedSplitting || indexToDelete >= 0) {
|
||||
await kclManager.executeAstMock(moddedAst)
|
||||
await codeManager.updateEditorWithAstAndWriteToFile(moddedAst)
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ child_process.spawnSync('git', [
|
||||
'clone',
|
||||
'--single-branch',
|
||||
'--branch',
|
||||
'achalmers/offset-plane-kwargs',
|
||||
'next',
|
||||
URL_GIT_KCL_SAMPLES,
|
||||
DIR_KCL_SAMPLES,
|
||||
])
|
||||
|
@ -375,14 +375,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
||||
},
|
||||
icon: 'line',
|
||||
status: 'available',
|
||||
disabled: (state) =>
|
||||
state.matches('Sketch no face') ||
|
||||
state.matches({
|
||||
Sketch: { 'Rectangle tool': 'Awaiting second corner' },
|
||||
}) ||
|
||||
state.matches({
|
||||
Sketch: { 'Circle tool': 'Awaiting Radius' },
|
||||
}),
|
||||
disabled: (state) => state.matches('Sketch no face'),
|
||||
title: 'Line',
|
||||
hotkey: (state) =>
|
||||
state.matches({ Sketch: 'Line tool' }) ? ['Esc', 'L'] : 'L',
|
||||
|
@ -82,7 +82,7 @@ const createWindow = (pathToOpen?: string, reuse?: boolean): BrowserWindow => {
|
||||
}
|
||||
if (!newWindow) {
|
||||
newWindow = new BrowserWindow({
|
||||
autoHideMenuBar: true,
|
||||
autoHideMenuBar: false,
|
||||
show: false,
|
||||
width: 1800,
|
||||
height: 1200,
|
||||
|
6
src/wasm-lib/Cargo.lock
generated
@ -730,7 +730,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "derive-docs"
|
||||
version = "0.1.36"
|
||||
version = "0.1.38"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"anyhow",
|
||||
@ -1712,7 +1712,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.2.35"
|
||||
version = "0.2.38"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx 0.5.1",
|
||||
@ -1779,7 +1779,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-test-server"
|
||||
version = "0.1.21"
|
||||
version = "0.1.38"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hyper 0.14.32",
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "derive-docs"
|
||||
description = "A tool for generating documentation from Rust derive macros"
|
||||
version = "0.1.36"
|
||||
version = "0.1.38"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-test-server"
|
||||
description = "A test server for KCL"
|
||||
version = "0.1.21"
|
||||
version = "0.1.38"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
|
@ -1,7 +1,4 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
use indexmap::IndexMap;
|
||||
@ -24,22 +21,20 @@ const NEED_PLANES: bool = true;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EngineConnection {
|
||||
batch: Arc<Mutex<Vec<(WebSocketRequest, kcl_lib::SourceRange)>>>,
|
||||
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::SourceRange)>>>,
|
||||
core_test: Arc<Mutex<String>>,
|
||||
batch: Arc<RwLock<Vec<(WebSocketRequest, kcl_lib::SourceRange)>>>,
|
||||
batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::SourceRange)>>>,
|
||||
core_test: Arc<RwLock<String>>,
|
||||
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
||||
execution_kind: Arc<Mutex<ExecutionKind>>,
|
||||
execution_kind: Arc<RwLock<ExecutionKind>>,
|
||||
}
|
||||
|
||||
impl EngineConnection {
|
||||
pub async fn new(result: Arc<Mutex<String>>) -> Result<EngineConnection> {
|
||||
if let Ok(mut code) = result.lock() {
|
||||
code.push_str(CPP_PREFIX);
|
||||
}
|
||||
pub async fn new(result: Arc<RwLock<String>>) -> Result<EngineConnection> {
|
||||
result.write().await.push_str(CPP_PREFIX);
|
||||
|
||||
Ok(EngineConnection {
|
||||
batch: Arc::new(Mutex::new(Vec::new())),
|
||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
||||
batch: Arc::new(RwLock::new(Vec::new())),
|
||||
batch_end: Arc::new(RwLock::new(IndexMap::new())),
|
||||
core_test: result,
|
||||
default_planes: Default::default(),
|
||||
execution_kind: Default::default(),
|
||||
@ -362,29 +357,29 @@ fn codegen_cpp_repl_uuid_setters(reps_id: &str, entity_ids: &[uuid::Uuid]) -> St
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl kcl_lib::EngineManager for EngineConnection {
|
||||
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, kcl_lib::SourceRange)>>> {
|
||||
fn batch(&self) -> Arc<RwLock<Vec<(WebSocketRequest, kcl_lib::SourceRange)>>> {
|
||||
self.batch.clone()
|
||||
}
|
||||
|
||||
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::SourceRange)>>> {
|
||||
fn batch_end(&self) -> Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::SourceRange)>>> {
|
||||
self.batch_end.clone()
|
||||
}
|
||||
|
||||
fn responses(&self) -> IndexMap<Uuid, WebSocketResponse> {
|
||||
IndexMap::new()
|
||||
fn responses(&self) -> Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>> {
|
||||
Arc::new(RwLock::new(IndexMap::new()))
|
||||
}
|
||||
|
||||
fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
||||
Vec::new()
|
||||
fn artifact_commands(&self) -> Arc<RwLock<Vec<ArtifactCommand>>> {
|
||||
Arc::new(RwLock::new(Vec::new()))
|
||||
}
|
||||
|
||||
fn execution_kind(&self) -> ExecutionKind {
|
||||
let guard = self.execution_kind.lock().unwrap();
|
||||
async fn execution_kind(&self) -> ExecutionKind {
|
||||
let guard = self.execution_kind.read().await;
|
||||
*guard
|
||||
}
|
||||
|
||||
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||
let mut guard = self.execution_kind.lock().unwrap();
|
||||
async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||
let mut guard = self.execution_kind.write().await;
|
||||
let original = *guard;
|
||||
*guard = execution_kind;
|
||||
original
|
||||
@ -435,24 +430,18 @@ impl kcl_lib::EngineManager for EngineConnection {
|
||||
}) => {
|
||||
let mut responses = HashMap::new();
|
||||
for request in requests {
|
||||
let (new_code, this_response);
|
||||
let (new_code, this_response) = self.handle_command(&request.cmd_id, &request.cmd);
|
||||
|
||||
if let Ok(mut test_code) = self.core_test.lock() {
|
||||
(new_code, this_response) = self.handle_command(&request.cmd_id, &request.cmd);
|
||||
|
||||
if !new_code.is_empty() {
|
||||
let new_code = new_code
|
||||
.trim()
|
||||
.split(' ')
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
+ "\n";
|
||||
//println!("{new_code}");
|
||||
test_code.push_str(&new_code);
|
||||
}
|
||||
} else {
|
||||
this_response = OkModelingCmdResponse::Empty {};
|
||||
if !new_code.is_empty() {
|
||||
let new_code = new_code
|
||||
.trim()
|
||||
.split(' ')
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
+ "\n";
|
||||
//println!("{new_code}");
|
||||
self.core_test.write().await.push_str(&new_code);
|
||||
}
|
||||
|
||||
responses.insert(
|
||||
@ -470,24 +459,18 @@ impl kcl_lib::EngineManager for EngineConnection {
|
||||
}
|
||||
WebSocketRequest::ModelingCmdReq(ModelingCmdReq { cmd, cmd_id }) => {
|
||||
//also handle unbatched requests inline
|
||||
let (new_code, this_response);
|
||||
let (new_code, this_response) = self.handle_command(&cmd_id, &cmd);
|
||||
|
||||
if let Ok(mut test_code) = self.core_test.lock() {
|
||||
(new_code, this_response) = self.handle_command(&cmd_id, &cmd);
|
||||
|
||||
if !new_code.is_empty() {
|
||||
let new_code = new_code
|
||||
.trim()
|
||||
.split(' ')
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
+ "\n";
|
||||
//println!("{new_code}");
|
||||
test_code.push_str(&new_code);
|
||||
}
|
||||
} else {
|
||||
this_response = OkModelingCmdResponse::Empty {};
|
||||
if !new_code.is_empty() {
|
||||
let new_code = new_code
|
||||
.trim()
|
||||
.split(' ')
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
+ "\n";
|
||||
//println!("{new_code}");
|
||||
self.core_test.write().await.push_str(&new_code);
|
||||
}
|
||||
|
||||
Ok(WebSocketResponse::Success(kcmc::websocket::SuccessWebSocketResponse {
|
||||
|
@ -1,7 +1,8 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use kcl_lib::{ExecState, ExecutorContext};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod conn_mock_core;
|
||||
@ -10,7 +11,7 @@ mod conn_mock_core;
|
||||
pub async fn kcl_to_engine_core(code: &str) -> Result<String> {
|
||||
let program = kcl_lib::Program::parse_no_errs(code)?;
|
||||
|
||||
let result = Arc::new(Mutex::new("".into()));
|
||||
let result = Arc::new(RwLock::new("".into()));
|
||||
let ref_result = Arc::clone(&result);
|
||||
|
||||
let ctx = ExecutorContext::new_forwarded_mock(Arc::new(Box::new(
|
||||
@ -18,6 +19,6 @@ pub async fn kcl_to_engine_core(code: &str) -> Result<String> {
|
||||
)));
|
||||
ctx.run(&program, &mut ExecState::new(&ctx.settings)).await?;
|
||||
|
||||
let result = result.lock().expect("mutex lock").clone();
|
||||
let result = result.read().await.clone();
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language implementation and tools"
|
||||
version = "0.2.35"
|
||||
version = "0.2.38"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -1,13 +1,9 @@
|
||||
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
||||
//! engine.
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use dashmap::DashMap;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use indexmap::IndexMap;
|
||||
use kcmc::{
|
||||
@ -17,9 +13,7 @@ use kcmc::{
|
||||
},
|
||||
ModelingCmd,
|
||||
};
|
||||
use kittycad_modeling_cmds::{
|
||||
self as kcmc, id::ModelingCmdId, ok_response::OkModelingCmdResponse, websocket::ModelingBatch,
|
||||
};
|
||||
use kittycad_modeling_cmds::{self as kcmc};
|
||||
use tokio::sync::{mpsc, oneshot, RwLock};
|
||||
use tokio_tungstenite::tungstenite::Message as WsMsg;
|
||||
use uuid::Uuid;
|
||||
@ -43,21 +37,21 @@ type WebSocketTcpWrite = futures::stream::SplitSink<tokio_tungstenite::WebSocket
|
||||
pub struct EngineConnection {
|
||||
engine_req_tx: mpsc::Sender<ToEngineReq>,
|
||||
shutdown_tx: mpsc::Sender<()>,
|
||||
responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>>,
|
||||
pending_errors: Arc<Mutex<Vec<String>>>,
|
||||
responses: Arc<RwLock<IndexMap<uuid::Uuid, WebSocketResponse>>>,
|
||||
pending_errors: Arc<RwLock<Vec<String>>>,
|
||||
#[allow(dead_code)]
|
||||
tcp_read_handle: Arc<TcpReadHandle>,
|
||||
socket_health: Arc<Mutex<SocketHealth>>,
|
||||
batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
|
||||
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||
artifact_commands: Arc<Mutex<Vec<ArtifactCommand>>>,
|
||||
socket_health: Arc<RwLock<SocketHealth>>,
|
||||
batch: Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>>,
|
||||
batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||
artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>,
|
||||
|
||||
/// The default planes for the scene.
|
||||
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
||||
/// If the server sends session data, it'll be copied to here.
|
||||
session_data: Arc<Mutex<Option<ModelingSessionData>>>,
|
||||
session_data: Arc<RwLock<Option<ModelingSessionData>>>,
|
||||
|
||||
execution_kind: Arc<Mutex<ExecutionKind>>,
|
||||
execution_kind: Arc<RwLock<ExecutionKind>>,
|
||||
}
|
||||
|
||||
pub struct TcpRead {
|
||||
@ -230,12 +224,12 @@ impl EngineConnection {
|
||||
|
||||
let mut tcp_read = TcpRead { stream: tcp_read };
|
||||
|
||||
let session_data: Arc<Mutex<Option<ModelingSessionData>>> = Arc::new(Mutex::new(None));
|
||||
let session_data: Arc<RwLock<Option<ModelingSessionData>>> = Arc::new(RwLock::new(None));
|
||||
let session_data2 = session_data.clone();
|
||||
let responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>> = Arc::new(DashMap::new());
|
||||
let responses: Arc<RwLock<IndexMap<uuid::Uuid, WebSocketResponse>>> = Arc::new(RwLock::new(IndexMap::new()));
|
||||
let responses_clone = responses.clone();
|
||||
let socket_health = Arc::new(Mutex::new(SocketHealth::Active));
|
||||
let pending_errors = Arc::new(Mutex::new(Vec::new()));
|
||||
let socket_health = Arc::new(RwLock::new(SocketHealth::Active));
|
||||
let pending_errors = Arc::new(RwLock::new(Vec::new()));
|
||||
let pending_errors_clone = pending_errors.clone();
|
||||
|
||||
let socket_health_tcp_read = socket_health.clone();
|
||||
@ -260,7 +254,7 @@ impl EngineConnection {
|
||||
let id: uuid::Uuid = (*resp_id).into();
|
||||
match batch_response {
|
||||
BatchResponse::Success { response } => {
|
||||
responses_clone.insert(
|
||||
responses_clone.write().await.insert(
|
||||
id,
|
||||
WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||
success: true,
|
||||
@ -272,7 +266,7 @@ impl EngineConnection {
|
||||
);
|
||||
}
|
||||
BatchResponse::Failure { errors } => {
|
||||
responses_clone.insert(
|
||||
responses_clone.write().await.insert(
|
||||
id,
|
||||
WebSocketResponse::Failure(FailureWebSocketResponse {
|
||||
success: false,
|
||||
@ -288,7 +282,7 @@ impl EngineConnection {
|
||||
resp: OkWebSocketResponseData::ModelingSessionData { session },
|
||||
..
|
||||
}) => {
|
||||
let mut sd = session_data2.lock().unwrap();
|
||||
let mut sd = session_data2.write().await;
|
||||
sd.replace(session.clone());
|
||||
}
|
||||
WebSocketResponse::Failure(FailureWebSocketResponse {
|
||||
@ -297,7 +291,7 @@ impl EngineConnection {
|
||||
errors,
|
||||
}) => {
|
||||
if let Some(id) = request_id {
|
||||
responses_clone.insert(
|
||||
responses_clone.write().await.insert(
|
||||
*id,
|
||||
WebSocketResponse::Failure(FailureWebSocketResponse {
|
||||
success: false,
|
||||
@ -307,19 +301,20 @@ impl EngineConnection {
|
||||
);
|
||||
} else {
|
||||
// Add it to our pending errors.
|
||||
let mut pe = pending_errors_clone.lock().unwrap();
|
||||
let mut pe = pending_errors_clone.write().await;
|
||||
for error in errors {
|
||||
if !pe.contains(&error.message) {
|
||||
pe.push(error.message.clone());
|
||||
}
|
||||
}
|
||||
drop(pe);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Some(id) = id {
|
||||
responses_clone.insert(id, ws_resp.clone());
|
||||
responses_clone.write().await.insert(id, ws_resp.clone());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
@ -327,7 +322,7 @@ impl EngineConnection {
|
||||
WebSocketReadError::Read(e) => crate::logln!("could not read from WS: {:?}", e),
|
||||
WebSocketReadError::Deser(e) => crate::logln!("could not deserialize msg from WS: {:?}", e),
|
||||
}
|
||||
*socket_health_tcp_read.lock().unwrap() = SocketHealth::Inactive;
|
||||
*socket_health_tcp_read.write().await = SocketHealth::Inactive;
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
@ -343,70 +338,41 @@ impl EngineConnection {
|
||||
responses,
|
||||
pending_errors,
|
||||
socket_health,
|
||||
batch: Arc::new(Mutex::new(Vec::new())),
|
||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
||||
artifact_commands: Arc::new(Mutex::new(Vec::new())),
|
||||
batch: Arc::new(RwLock::new(Vec::new())),
|
||||
batch_end: Arc::new(RwLock::new(IndexMap::new())),
|
||||
artifact_commands: Arc::new(RwLock::new(Vec::new())),
|
||||
default_planes: Default::default(),
|
||||
session_data,
|
||||
execution_kind: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_command(
|
||||
&self,
|
||||
cmd: &ModelingCmd,
|
||||
cmd_id: ModelingCmdId,
|
||||
id_to_source_range: &HashMap<Uuid, SourceRange>,
|
||||
) -> Result<(), KclError> {
|
||||
let cmd_id = *cmd_id.as_ref();
|
||||
let range = id_to_source_range
|
||||
.get(&cmd_id)
|
||||
.copied()
|
||||
.ok_or_else(|| KclError::internal(format!("Failed to get source range for command ID: {:?}", cmd_id)))?;
|
||||
|
||||
// Add artifact command.
|
||||
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
||||
artifact_commands.push(ArtifactCommand {
|
||||
cmd_id,
|
||||
range,
|
||||
command: cmd.clone(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl EngineManager for EngineConnection {
|
||||
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>> {
|
||||
fn batch(&self) -> Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>> {
|
||||
self.batch.clone()
|
||||
}
|
||||
|
||||
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
|
||||
fn batch_end(&self) -> Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
|
||||
self.batch_end.clone()
|
||||
}
|
||||
|
||||
fn responses(&self) -> IndexMap<Uuid, WebSocketResponse> {
|
||||
self.responses
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
let (k, v) = entry.pair();
|
||||
(*k, v.clone())
|
||||
})
|
||||
.collect()
|
||||
fn responses(&self) -> Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>> {
|
||||
self.responses.clone()
|
||||
}
|
||||
|
||||
fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
||||
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
||||
std::mem::take(&mut *artifact_commands)
|
||||
fn artifact_commands(&self) -> Arc<RwLock<Vec<ArtifactCommand>>> {
|
||||
self.artifact_commands.clone()
|
||||
}
|
||||
|
||||
fn execution_kind(&self) -> ExecutionKind {
|
||||
let guard = self.execution_kind.lock().unwrap();
|
||||
async fn execution_kind(&self) -> ExecutionKind {
|
||||
let guard = self.execution_kind.read().await;
|
||||
*guard
|
||||
}
|
||||
|
||||
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||
let mut guard = self.execution_kind.lock().unwrap();
|
||||
async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||
let mut guard = self.execution_kind.write().await;
|
||||
let original = *guard;
|
||||
*guard = execution_kind;
|
||||
original
|
||||
@ -447,49 +413,8 @@ impl EngineManager for EngineConnection {
|
||||
id: uuid::Uuid,
|
||||
source_range: SourceRange,
|
||||
cmd: WebSocketRequest,
|
||||
id_to_source_range: HashMap<Uuid, SourceRange>,
|
||||
_id_to_source_range: HashMap<Uuid, SourceRange>,
|
||||
) -> Result<WebSocketResponse, KclError> {
|
||||
match &cmd {
|
||||
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
||||
for request in requests {
|
||||
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
||||
}
|
||||
}
|
||||
WebSocketRequest::ModelingCmdReq(request) => {
|
||||
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// In isolated mode, we don't send the command to the engine.
|
||||
if self.execution_kind().is_isolated() {
|
||||
return match &cmd {
|
||||
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
||||
let mut responses = HashMap::with_capacity(requests.len());
|
||||
for request in requests {
|
||||
responses.insert(
|
||||
request.cmd_id,
|
||||
BatchResponse::Success {
|
||||
response: OkModelingCmdResponse::Empty {},
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||
request_id: Some(id),
|
||||
resp: OkWebSocketResponseData::ModelingBatch { responses },
|
||||
success: true,
|
||||
}))
|
||||
}
|
||||
_ => Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||
request_id: Some(id),
|
||||
resp: OkWebSocketResponseData::Modeling {
|
||||
modeling_response: OkModelingCmdResponse::Empty {},
|
||||
},
|
||||
success: true,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
// Send the request to the engine, via the actor.
|
||||
@ -524,25 +449,24 @@ impl EngineManager for EngineConnection {
|
||||
// Wait for the response.
|
||||
let current_time = std::time::Instant::now();
|
||||
while current_time.elapsed().as_secs() < 60 {
|
||||
if let Ok(guard) = self.socket_health.lock() {
|
||||
if *guard == SocketHealth::Inactive {
|
||||
// Check if we have any pending errors.
|
||||
let pe = self.pending_errors.lock().unwrap();
|
||||
if !pe.is_empty() {
|
||||
return Err(KclError::Engine(KclErrorDetails {
|
||||
message: pe.join(", ").to_string(),
|
||||
source_ranges: vec![source_range],
|
||||
}));
|
||||
} else {
|
||||
return Err(KclError::Engine(KclErrorDetails {
|
||||
message: "Modeling command failed: websocket closed early".to_string(),
|
||||
source_ranges: vec![source_range],
|
||||
}));
|
||||
}
|
||||
let guard = self.socket_health.read().await;
|
||||
if *guard == SocketHealth::Inactive {
|
||||
// Check if we have any pending errors.
|
||||
let pe = self.pending_errors.read().await;
|
||||
if !pe.is_empty() {
|
||||
return Err(KclError::Engine(KclErrorDetails {
|
||||
message: pe.join(", ").to_string(),
|
||||
source_ranges: vec![source_range],
|
||||
}));
|
||||
} else {
|
||||
return Err(KclError::Engine(KclErrorDetails {
|
||||
message: "Modeling command failed: websocket closed early".to_string(),
|
||||
source_ranges: vec![source_range],
|
||||
}));
|
||||
}
|
||||
}
|
||||
// We pop off the responses to cleanup our mappings.
|
||||
if let Some((_, resp)) = self.responses.remove(&id) {
|
||||
if let Some(resp) = self.responses.write().await.shift_remove(&id) {
|
||||
return Ok(resp);
|
||||
}
|
||||
}
|
||||
@ -553,17 +477,16 @@ impl EngineManager for EngineConnection {
|
||||
}))
|
||||
}
|
||||
|
||||
fn get_session_data(&self) -> Option<ModelingSessionData> {
|
||||
self.session_data.lock().unwrap().clone()
|
||||
async fn get_session_data(&self) -> Option<ModelingSessionData> {
|
||||
self.session_data.read().await.clone()
|
||||
}
|
||||
|
||||
async fn close(&self) {
|
||||
let _ = self.shutdown_tx.send(()).await;
|
||||
loop {
|
||||
if let Ok(guard) = self.socket_health.lock() {
|
||||
if *guard == SocketHealth::Inactive {
|
||||
return;
|
||||
}
|
||||
let guard = self.socket_health.read().await;
|
||||
if *guard == SocketHealth::Inactive {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,7 @@
|
||||
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
||||
//! engine.
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
use indexmap::IndexMap;
|
||||
@ -15,7 +12,8 @@ use kcmc::{
|
||||
WebSocketResponse,
|
||||
},
|
||||
};
|
||||
use kittycad_modeling_cmds::{self as kcmc, id::ModelingCmdId, ModelingCmd};
|
||||
use kittycad_modeling_cmds::{self as kcmc};
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::ExecutionKind;
|
||||
@ -28,71 +26,48 @@ use crate::{
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EngineConnection {
|
||||
batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
|
||||
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||
artifact_commands: Arc<Mutex<Vec<ArtifactCommand>>>,
|
||||
execution_kind: Arc<Mutex<ExecutionKind>>,
|
||||
batch: Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>>,
|
||||
batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||
artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>,
|
||||
execution_kind: Arc<RwLock<ExecutionKind>>,
|
||||
}
|
||||
|
||||
impl EngineConnection {
|
||||
pub async fn new() -> Result<EngineConnection> {
|
||||
Ok(EngineConnection {
|
||||
batch: Arc::new(Mutex::new(Vec::new())),
|
||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
||||
artifact_commands: Arc::new(Mutex::new(Vec::new())),
|
||||
batch: Arc::new(RwLock::new(Vec::new())),
|
||||
batch_end: Arc::new(RwLock::new(IndexMap::new())),
|
||||
artifact_commands: Arc::new(RwLock::new(Vec::new())),
|
||||
execution_kind: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_command(
|
||||
&self,
|
||||
cmd: &ModelingCmd,
|
||||
cmd_id: ModelingCmdId,
|
||||
id_to_source_range: &HashMap<Uuid, SourceRange>,
|
||||
) -> Result<(), KclError> {
|
||||
let cmd_id = *cmd_id.as_ref();
|
||||
let range = id_to_source_range
|
||||
.get(&cmd_id)
|
||||
.copied()
|
||||
.ok_or_else(|| KclError::internal(format!("Failed to get source range for command ID: {:?}", cmd_id)))?;
|
||||
|
||||
// Add artifact command.
|
||||
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
||||
artifact_commands.push(ArtifactCommand {
|
||||
cmd_id,
|
||||
range,
|
||||
command: cmd.clone(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl crate::engine::EngineManager for EngineConnection {
|
||||
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>> {
|
||||
fn batch(&self) -> Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>> {
|
||||
self.batch.clone()
|
||||
}
|
||||
|
||||
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
|
||||
fn batch_end(&self) -> Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
|
||||
self.batch_end.clone()
|
||||
}
|
||||
|
||||
fn responses(&self) -> IndexMap<Uuid, WebSocketResponse> {
|
||||
IndexMap::new()
|
||||
fn responses(&self) -> Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>> {
|
||||
Arc::new(RwLock::new(IndexMap::new()))
|
||||
}
|
||||
|
||||
fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
||||
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
||||
std::mem::take(&mut *artifact_commands)
|
||||
fn artifact_commands(&self) -> Arc<RwLock<Vec<ArtifactCommand>>> {
|
||||
self.artifact_commands.clone()
|
||||
}
|
||||
|
||||
fn execution_kind(&self) -> ExecutionKind {
|
||||
let guard = self.execution_kind.lock().unwrap();
|
||||
async fn execution_kind(&self) -> ExecutionKind {
|
||||
let guard = self.execution_kind.read().await;
|
||||
*guard
|
||||
}
|
||||
|
||||
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||
let mut guard = self.execution_kind.lock().unwrap();
|
||||
async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||
let mut guard = self.execution_kind.write().await;
|
||||
let original = *guard;
|
||||
*guard = execution_kind;
|
||||
original
|
||||
@ -119,7 +94,7 @@ impl crate::engine::EngineManager for EngineConnection {
|
||||
id: uuid::Uuid,
|
||||
_source_range: SourceRange,
|
||||
cmd: WebSocketRequest,
|
||||
id_to_source_range: HashMap<Uuid, SourceRange>,
|
||||
_id_to_source_range: HashMap<Uuid, SourceRange>,
|
||||
) -> Result<WebSocketResponse, KclError> {
|
||||
match cmd {
|
||||
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch {
|
||||
@ -130,7 +105,6 @@ impl crate::engine::EngineManager for EngineConnection {
|
||||
// Create the empty responses.
|
||||
let mut responses = HashMap::with_capacity(requests.len());
|
||||
for request in requests {
|
||||
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
||||
responses.insert(
|
||||
request.cmd_id,
|
||||
BatchResponse::Success {
|
||||
@ -144,17 +118,13 @@ impl crate::engine::EngineManager for EngineConnection {
|
||||
success: true,
|
||||
}))
|
||||
}
|
||||
WebSocketRequest::ModelingCmdReq(request) => {
|
||||
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
||||
|
||||
Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||
request_id: Some(id),
|
||||
resp: OkWebSocketResponseData::Modeling {
|
||||
modeling_response: OkModelingCmdResponse::Empty {},
|
||||
},
|
||||
success: true,
|
||||
}))
|
||||
}
|
||||
WebSocketRequest::ModelingCmdReq(_) => Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||
request_id: Some(id),
|
||||
resp: OkWebSocketResponseData::Modeling {
|
||||
modeling_response: OkModelingCmdResponse::Empty {},
|
||||
},
|
||||
success: true,
|
||||
})),
|
||||
_ => Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||
request_id: Some(id),
|
||||
resp: OkWebSocketResponseData::Modeling {
|
||||
|
@ -1,22 +1,12 @@
|
||||
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
||||
//! engine.
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
use indexmap::IndexMap;
|
||||
use kcmc::{
|
||||
id::ModelingCmdId,
|
||||
ok_response::OkModelingCmdResponse,
|
||||
websocket::{
|
||||
BatchResponse, ModelingBatch, OkWebSocketResponseData, SuccessWebSocketResponse, WebSocketRequest,
|
||||
WebSocketResponse,
|
||||
},
|
||||
ModelingCmd,
|
||||
};
|
||||
use kcmc::websocket::{WebSocketRequest, WebSocketResponse};
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
@ -54,11 +44,11 @@ extern "C" {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EngineConnection {
|
||||
manager: Arc<EngineCommandManager>,
|
||||
batch: Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>,
|
||||
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||
responses: Arc<Mutex<IndexMap<Uuid, WebSocketResponse>>>,
|
||||
artifact_commands: Arc<Mutex<Vec<ArtifactCommand>>>,
|
||||
execution_kind: Arc<Mutex<ExecutionKind>>,
|
||||
batch: Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>>,
|
||||
batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||
responses: Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>>,
|
||||
artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>,
|
||||
execution_kind: Arc<RwLock<ExecutionKind>>,
|
||||
}
|
||||
|
||||
// Safety: WebAssembly will only ever run in a single-threaded context.
|
||||
@ -70,66 +60,101 @@ impl EngineConnection {
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
Ok(EngineConnection {
|
||||
manager: Arc::new(manager),
|
||||
batch: Arc::new(Mutex::new(Vec::new())),
|
||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
||||
responses: Arc::new(Mutex::new(IndexMap::new())),
|
||||
artifact_commands: Arc::new(Mutex::new(Vec::new())),
|
||||
batch: Arc::new(RwLock::new(Vec::new())),
|
||||
batch_end: Arc::new(RwLock::new(IndexMap::new())),
|
||||
responses: Arc::new(RwLock::new(IndexMap::new())),
|
||||
artifact_commands: Arc::new(RwLock::new(Vec::new())),
|
||||
execution_kind: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl EngineConnection {
|
||||
fn handle_command(
|
||||
async fn do_send_modeling_cmd(
|
||||
&self,
|
||||
cmd: &ModelingCmd,
|
||||
cmd_id: ModelingCmdId,
|
||||
id_to_source_range: &HashMap<Uuid, SourceRange>,
|
||||
) -> Result<(), KclError> {
|
||||
let cmd_id = *cmd_id.as_ref();
|
||||
let range = id_to_source_range
|
||||
.get(&cmd_id)
|
||||
.copied()
|
||||
.ok_or_else(|| KclError::internal(format!("Failed to get source range for command ID: {:?}", cmd_id)))?;
|
||||
id: uuid::Uuid,
|
||||
source_range: SourceRange,
|
||||
cmd: WebSocketRequest,
|
||||
id_to_source_range: HashMap<uuid::Uuid, SourceRange>,
|
||||
) -> Result<WebSocketResponse, KclError> {
|
||||
let source_range_str = serde_json::to_string(&source_range).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to serialize source range: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
let cmd_str = serde_json::to_string(&cmd).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to serialize modeling command: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
let id_to_source_range_str = serde_json::to_string(&id_to_source_range).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to serialize id to source range: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
// Add artifact command.
|
||||
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
||||
artifact_commands.push(ArtifactCommand {
|
||||
cmd_id,
|
||||
range,
|
||||
command: cmd.clone(),
|
||||
});
|
||||
Ok(())
|
||||
let promise = self
|
||||
.manager
|
||||
.send_modeling_cmd_from_wasm(id.to_string(), source_range_str, cmd_str, id_to_source_range_str)
|
||||
.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: e.to_string().into(),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
let value = crate::wasm::JsFuture::from(promise).await.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to wait for promise from engine: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
// Parse the value as a string.
|
||||
let s = value.as_string().ok_or_else(|| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to get string from response from engine: `{:?}`", value),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
let ws_result: WebSocketResponse = serde_json::from_str(&s).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to deserialize response from engine: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(ws_result)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl crate::engine::EngineManager for EngineConnection {
|
||||
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>> {
|
||||
fn batch(&self) -> Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>> {
|
||||
self.batch.clone()
|
||||
}
|
||||
|
||||
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
|
||||
fn batch_end(&self) -> Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>> {
|
||||
self.batch_end.clone()
|
||||
}
|
||||
|
||||
fn responses(&self) -> IndexMap<Uuid, WebSocketResponse> {
|
||||
let responses = self.responses.lock().unwrap();
|
||||
responses.clone()
|
||||
fn responses(&self) -> Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>> {
|
||||
self.responses.clone()
|
||||
}
|
||||
|
||||
fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
||||
let mut artifact_commands = self.artifact_commands.lock().unwrap();
|
||||
std::mem::take(&mut *artifact_commands)
|
||||
fn artifact_commands(&self) -> Arc<RwLock<Vec<ArtifactCommand>>> {
|
||||
self.artifact_commands.clone()
|
||||
}
|
||||
|
||||
fn execution_kind(&self) -> ExecutionKind {
|
||||
let guard = self.execution_kind.lock().unwrap();
|
||||
async fn execution_kind(&self) -> ExecutionKind {
|
||||
let guard = self.execution_kind.read().await;
|
||||
*guard
|
||||
}
|
||||
|
||||
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||
let mut guard = self.execution_kind.lock().unwrap();
|
||||
async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||
let mut guard = self.execution_kind.write().await;
|
||||
let original = *guard;
|
||||
*guard = execution_kind;
|
||||
original
|
||||
@ -214,100 +239,18 @@ impl crate::engine::EngineManager for EngineConnection {
|
||||
cmd: WebSocketRequest,
|
||||
id_to_source_range: HashMap<uuid::Uuid, SourceRange>,
|
||||
) -> Result<WebSocketResponse, KclError> {
|
||||
match &cmd {
|
||||
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
||||
for request in requests {
|
||||
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
||||
}
|
||||
}
|
||||
WebSocketRequest::ModelingCmdReq(request) => {
|
||||
self.handle_command(&request.cmd, request.cmd_id, &id_to_source_range)?;
|
||||
}
|
||||
_ => {}
|
||||
let ws_result = self
|
||||
.do_send_modeling_cmd(id, source_range, cmd, id_to_source_range)
|
||||
.await?;
|
||||
|
||||
// In isolated mode, we don't save the response.
|
||||
if self.execution_kind().await.is_isolated() {
|
||||
return Ok(ws_result);
|
||||
}
|
||||
|
||||
// In isolated mode, we don't send the command to the engine.
|
||||
if self.execution_kind().is_isolated() {
|
||||
return match &cmd {
|
||||
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
||||
let mut responses = HashMap::with_capacity(requests.len());
|
||||
for request in requests {
|
||||
responses.insert(
|
||||
request.cmd_id,
|
||||
BatchResponse::Success {
|
||||
response: OkModelingCmdResponse::Empty {},
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||
request_id: Some(id),
|
||||
resp: OkWebSocketResponseData::ModelingBatch { responses },
|
||||
success: true,
|
||||
}))
|
||||
}
|
||||
_ => Ok(WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||
request_id: Some(id),
|
||||
resp: OkWebSocketResponseData::Modeling {
|
||||
modeling_response: OkModelingCmdResponse::Empty {},
|
||||
},
|
||||
success: true,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
let source_range_str = serde_json::to_string(&source_range).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to serialize source range: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
let cmd_str = serde_json::to_string(&cmd).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to serialize modeling command: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
let id_to_source_range_str = serde_json::to_string(&id_to_source_range).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to serialize id to source range: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
let promise = self
|
||||
.manager
|
||||
.send_modeling_cmd_from_wasm(id.to_string(), source_range_str, cmd_str, id_to_source_range_str)
|
||||
.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: e.to_string().into(),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
let value = crate::wasm::JsFuture::from(promise).await.map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to wait for promise from engine: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
// Parse the value as a string.
|
||||
let s = value.as_string().ok_or_else(|| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to get string from response from engine: `{:?}`", value),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
let ws_result: WebSocketResponse = serde_json::from_str(&s).map_err(|e| {
|
||||
KclError::Engine(KclErrorDetails {
|
||||
message: format!("Failed to deserialize response from engine: {:?}", e),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
let mut responses = self.responses.lock().unwrap();
|
||||
let mut responses = self.responses.write().await;
|
||||
responses.insert(id, ws_result.clone());
|
||||
drop(responses);
|
||||
|
||||
Ok(ws_result)
|
||||
}
|
||||
|
@ -8,14 +8,12 @@ pub mod conn_mock;
|
||||
#[cfg(feature = "engine")]
|
||||
pub mod conn_wasm;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use kcmc::{
|
||||
each_cmd as mcmd,
|
||||
id::ModelingCmdId,
|
||||
length_unit::LengthUnit,
|
||||
ok_response::OkModelingCmdResponse,
|
||||
shared::Color,
|
||||
@ -28,6 +26,7 @@ use kcmc::{
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
@ -62,28 +61,38 @@ impl ExecutionKind {
|
||||
#[async_trait::async_trait]
|
||||
pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
/// Get the batch of commands to be sent to the engine.
|
||||
fn batch(&self) -> Arc<Mutex<Vec<(WebSocketRequest, SourceRange)>>>;
|
||||
fn batch(&self) -> Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>>;
|
||||
|
||||
/// Get the batch of end commands to be sent to the engine.
|
||||
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>;
|
||||
fn batch_end(&self) -> Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>;
|
||||
|
||||
/// Get the command responses from the engine.
|
||||
fn responses(&self) -> IndexMap<Uuid, WebSocketResponse>;
|
||||
fn responses(&self) -> Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>>;
|
||||
|
||||
/// Take the artifact commands generated up to this point and clear them.
|
||||
fn take_artifact_commands(&self) -> Vec<ArtifactCommand>;
|
||||
/// Get the artifact commands that have accumulated so far.
|
||||
fn artifact_commands(&self) -> Arc<RwLock<Vec<ArtifactCommand>>>;
|
||||
|
||||
/// Clear all artifact commands that have accumulated so far.
|
||||
fn clear_artifact_commands(&self) {
|
||||
self.take_artifact_commands();
|
||||
async fn clear_artifact_commands(&self) {
|
||||
self.artifact_commands().write().await.clear();
|
||||
}
|
||||
|
||||
/// Take the artifact commands that have accumulated so far and clear them.
|
||||
async fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
||||
std::mem::take(&mut *self.artifact_commands().write().await)
|
||||
}
|
||||
|
||||
/// Take the responses that have accumulated so far and clear them.
|
||||
async fn take_responses(&self) -> IndexMap<Uuid, WebSocketResponse> {
|
||||
std::mem::take(&mut *self.responses().write().await)
|
||||
}
|
||||
|
||||
/// Get the current execution kind.
|
||||
fn execution_kind(&self) -> ExecutionKind;
|
||||
async fn execution_kind(&self) -> ExecutionKind;
|
||||
|
||||
/// Replace the current execution kind with a new value and return the
|
||||
/// existing value.
|
||||
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind;
|
||||
async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind;
|
||||
|
||||
/// Get the default planes.
|
||||
async fn default_planes(
|
||||
@ -127,7 +136,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
|
||||
// Ensure artifact commands are cleared so that we don't accumulate them
|
||||
// across runs.
|
||||
self.clear_artifact_commands();
|
||||
self.clear_artifact_commands().await;
|
||||
|
||||
// Do the after clear scene hook.
|
||||
self.clear_scene_post_hook(id_generator, source_range).await?;
|
||||
@ -151,6 +160,27 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_artifact_command(
|
||||
&self,
|
||||
cmd: &ModelingCmd,
|
||||
cmd_id: ModelingCmdId,
|
||||
id_to_source_range: &HashMap<Uuid, SourceRange>,
|
||||
) -> Result<(), KclError> {
|
||||
let cmd_id = *cmd_id.as_ref();
|
||||
let range = id_to_source_range
|
||||
.get(&cmd_id)
|
||||
.copied()
|
||||
.ok_or_else(|| KclError::internal(format!("Failed to get source range for command ID: {:?}", cmd_id)))?;
|
||||
|
||||
// Add artifact command.
|
||||
self.artifact_commands().write().await.push(ArtifactCommand {
|
||||
cmd_id,
|
||||
range,
|
||||
command: cmd.clone(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_units(
|
||||
&self,
|
||||
units: crate::UnitLength,
|
||||
@ -197,13 +227,18 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
source_range: SourceRange,
|
||||
cmd: &ModelingCmd,
|
||||
) -> Result<(), crate::errors::KclError> {
|
||||
// In isolated mode, we don't send the command to the engine.
|
||||
if self.execution_kind().await.is_isolated() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let req = WebSocketRequest::ModelingCmdReq(ModelingCmdReq {
|
||||
cmd: cmd.clone(),
|
||||
cmd_id: id.into(),
|
||||
});
|
||||
|
||||
// Add cmd to the batch.
|
||||
self.batch().lock().unwrap().push((req, source_range));
|
||||
self.batch().write().await.push((req, source_range));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -217,13 +252,18 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
source_range: SourceRange,
|
||||
cmd: &ModelingCmd,
|
||||
) -> Result<(), crate::errors::KclError> {
|
||||
// In isolated mode, we don't send the command to the engine.
|
||||
if self.execution_kind().await.is_isolated() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let req = WebSocketRequest::ModelingCmdReq(ModelingCmdReq {
|
||||
cmd: cmd.clone(),
|
||||
cmd_id: id.into(),
|
||||
});
|
||||
|
||||
// Add cmd to the batch end.
|
||||
self.batch_end().lock().unwrap().insert(id, (req, source_range));
|
||||
self.batch_end().write().await.insert(id, (req, source_range));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -249,11 +289,11 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
source_range: SourceRange,
|
||||
) -> Result<OkWebSocketResponseData, crate::errors::KclError> {
|
||||
let all_requests = if batch_end {
|
||||
let mut requests = self.batch().lock().unwrap().clone();
|
||||
requests.extend(self.batch_end().lock().unwrap().values().cloned());
|
||||
let mut requests = self.batch().read().await.clone();
|
||||
requests.extend(self.batch_end().read().await.values().cloned());
|
||||
requests
|
||||
} else {
|
||||
self.batch().lock().unwrap().clone()
|
||||
self.batch().read().await.clone()
|
||||
};
|
||||
|
||||
// Return early if we have no commands to send.
|
||||
@ -304,10 +344,27 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
}
|
||||
}
|
||||
|
||||
// Do the artifact commands.
|
||||
for (req, _) in all_requests.iter() {
|
||||
match &req {
|
||||
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
||||
for request in requests {
|
||||
self.handle_artifact_command(&request.cmd, request.cmd_id, &id_to_source_range)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
WebSocketRequest::ModelingCmdReq(request) => {
|
||||
self.handle_artifact_command(&request.cmd, request.cmd_id, &id_to_source_range)
|
||||
.await?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Throw away the old batch queue.
|
||||
self.batch().lock().unwrap().clear();
|
||||
self.batch().write().await.clear();
|
||||
if batch_end {
|
||||
self.batch_end().lock().unwrap().clear();
|
||||
self.batch_end().write().await.clear();
|
||||
}
|
||||
|
||||
// We pop off the responses to cleanup our mappings.
|
||||
@ -596,7 +653,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
|
||||
/// Get session data, if it has been received.
|
||||
/// Returns None if the server never sent it.
|
||||
fn get_session_data(&self) -> Option<ModelingSessionData> {
|
||||
async fn get_session_data(&self) -> Option<ModelingSessionData> {
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ impl ExecutorContext {
|
||||
let old_units = exec_state.length_unit();
|
||||
exec_state.mod_local.settings.update_from_annotation(annotation)?;
|
||||
let new_units = exec_state.length_unit();
|
||||
if !self.engine.execution_kind().is_isolated() && old_units != new_units {
|
||||
if !self.engine.execution_kind().await.is_isolated() && old_units != new_units {
|
||||
self.engine
|
||||
.set_units(new_units.into(), annotation.as_source_range())
|
||||
.await?;
|
||||
@ -393,7 +393,7 @@ impl ExecutorContext {
|
||||
exec_state.global.mod_loader.enter_module(path);
|
||||
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
|
||||
exec_state.mut_memory().push_new_root_env();
|
||||
let original_execution = self.engine.replace_execution_kind(exec_kind);
|
||||
let original_execution = self.engine.replace_execution_kind(exec_kind).await;
|
||||
|
||||
let result = self
|
||||
.exec_program(program, exec_state, crate::execution::BodyType::Root)
|
||||
@ -406,7 +406,7 @@ impl ExecutorContext {
|
||||
if !exec_kind.is_isolated() && new_units != old_units {
|
||||
self.engine.set_units(old_units.into(), Default::default()).await?;
|
||||
}
|
||||
self.engine.replace_execution_kind(original_execution);
|
||||
self.engine.replace_execution_kind(original_execution).await;
|
||||
|
||||
result
|
||||
.map_err(|err| {
|
||||
@ -1720,13 +1720,12 @@ impl JsonSchema for FunctionParam<'_> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::{
|
||||
execution::{memory::ProgramMemory, parse_execute},
|
||||
parsing::ast::types::{DefaultParamVal, Identifier, Parameter},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_assign_args_to_params() {
|
||||
// Set up a little framework for this test.
|
||||
|
@ -289,7 +289,7 @@ pub struct PreImportedGeometry {
|
||||
}
|
||||
|
||||
pub async fn send_to_engine(pre: PreImportedGeometry, ctxt: &ExecutorContext) -> Result<ImportedGeometry, KclError> {
|
||||
if ctxt.no_engine_commands() {
|
||||
if ctxt.no_engine_commands().await {
|
||||
return Ok(ImportedGeometry {
|
||||
id: pre.id,
|
||||
value: pre.command.files.iter().map(|f| f.path.to_string()).collect(),
|
||||
|
@ -484,8 +484,8 @@ impl ExecutorContext {
|
||||
}
|
||||
|
||||
/// Returns true if we should not send engine commands for any reason.
|
||||
pub fn no_engine_commands(&self) -> bool {
|
||||
self.is_mock() || self.engine.execution_kind().is_isolated()
|
||||
pub async fn no_engine_commands(&self) -> bool {
|
||||
self.is_mock() || self.engine.execution_kind().await.is_isolated()
|
||||
}
|
||||
|
||||
pub async fn send_clear_scene(
|
||||
@ -713,7 +713,7 @@ impl ExecutorContext {
|
||||
"Post interpretation KCL memory stats: {:#?}",
|
||||
exec_state.memory().stats
|
||||
));
|
||||
let session_data = self.engine.get_session_data();
|
||||
let session_data = self.engine.get_session_data().await;
|
||||
Ok(session_data)
|
||||
}
|
||||
|
||||
@ -734,8 +734,11 @@ impl ExecutorContext {
|
||||
exec_state
|
||||
.global
|
||||
.artifact_commands
|
||||
.extend(self.engine.take_artifact_commands());
|
||||
exec_state.global.artifact_responses.extend(self.engine.responses());
|
||||
.extend(self.engine.take_artifact_commands().await);
|
||||
exec_state
|
||||
.global
|
||||
.artifact_responses
|
||||
.extend(self.engine.take_responses().await);
|
||||
// Build the artifact graph.
|
||||
match build_artifact_graph(
|
||||
&exec_state.global.artifact_commands,
|
||||
|
@ -949,6 +949,28 @@ mod import_foreign {
|
||||
super::execute(TEST_NAME, false).await
|
||||
}
|
||||
}
|
||||
mod assembly_non_default_units {
|
||||
const TEST_NAME: &str = "assembly_non_default_units";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[test]
|
||||
fn unparse() {
|
||||
super::unparse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
|
||||
mod array_elem_push_fail {
|
||||
const TEST_NAME: &str = "array_elem_push_fail";
|
||||
|
||||
|
@ -277,12 +277,12 @@ impl Args {
|
||||
// before what ever we call next.
|
||||
for id in ids {
|
||||
// Pop it off the batch_end and add it to the batch.
|
||||
let Some(item) = self.ctx.engine.batch_end().lock().unwrap().shift_remove(&id) else {
|
||||
let Some(item) = self.ctx.engine.batch_end().write().await.shift_remove(&id) else {
|
||||
// It might be in the batch already.
|
||||
continue;
|
||||
};
|
||||
// Add it to the batch.
|
||||
self.ctx.engine.batch().lock().unwrap().push(item);
|
||||
self.ctx.engine.batch().write().await.push(item);
|
||||
}
|
||||
|
||||
// Run flush.
|
||||
|
@ -232,8 +232,9 @@ pub(crate) async fn do_post_extrude(
|
||||
sides: face_id_map,
|
||||
start_cap_id,
|
||||
end_cap_id,
|
||||
} = analyze_faces(exec_state, &args, face_infos);
|
||||
} = analyze_faces(exec_state, &args, face_infos).await;
|
||||
// Iterate over the sketch.value array and add face_id to GeoMeta
|
||||
let no_engine_commands = args.ctx.no_engine_commands().await;
|
||||
let new_value = sketch
|
||||
.paths
|
||||
.iter()
|
||||
@ -267,7 +268,7 @@ pub(crate) async fn do_post_extrude(
|
||||
Some(extrude_surface)
|
||||
}
|
||||
}
|
||||
} else if args.ctx.no_engine_commands() {
|
||||
} else if no_engine_commands {
|
||||
// Only pre-populate the extrude surface if we are in mock mode.
|
||||
|
||||
let extrude_surface = ExtrudeSurface::ExtrudePlane(crate::execution::ExtrudePlane {
|
||||
@ -313,12 +314,12 @@ struct Faces {
|
||||
start_cap_id: Option<Uuid>,
|
||||
}
|
||||
|
||||
fn analyze_faces(exec_state: &mut ExecState, args: &Args, face_infos: Vec<ExtrusionFaceInfo>) -> Faces {
|
||||
async fn analyze_faces(exec_state: &mut ExecState, args: &Args, face_infos: Vec<ExtrusionFaceInfo>) -> Faces {
|
||||
let mut faces = Faces {
|
||||
sides: HashMap::with_capacity(face_infos.len()),
|
||||
..Default::default()
|
||||
};
|
||||
if args.ctx.no_engine_commands() {
|
||||
if args.ctx.no_engine_commands().await {
|
||||
// Create fake IDs for start and end caps, to make extrudes mock-execute safe
|
||||
faces.start_cap_id = Some(exec_state.next_uuid());
|
||||
faces.end_cap_id = Some(exec_state.next_uuid());
|
||||
|
@ -222,7 +222,7 @@ pub async fn get_opposite_edge(exec_state: &mut ExecState, args: Args) -> Result
|
||||
name = "getOppositeEdge",
|
||||
}]
|
||||
async fn inner_get_opposite_edge(tag: TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<Uuid, KclError> {
|
||||
if args.ctx.no_engine_commands() {
|
||||
if args.ctx.no_engine_commands().await {
|
||||
return Ok(exec_state.next_uuid());
|
||||
}
|
||||
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
||||
@ -299,7 +299,7 @@ async fn inner_get_next_adjacent_edge(
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Uuid, KclError> {
|
||||
if args.ctx.no_engine_commands() {
|
||||
if args.ctx.no_engine_commands().await {
|
||||
return Ok(exec_state.next_uuid());
|
||||
}
|
||||
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
||||
@ -384,7 +384,7 @@ async fn inner_get_previous_adjacent_edge(
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Uuid, KclError> {
|
||||
if args.ctx.no_engine_commands() {
|
||||
if args.ctx.no_engine_commands().await {
|
||||
return Ok(exec_state.next_uuid());
|
||||
}
|
||||
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
||||
|
@ -125,7 +125,7 @@ async fn inner_helix(
|
||||
meta: vec![args.source_range.into()],
|
||||
});
|
||||
|
||||
if args.ctx.no_engine_commands() {
|
||||
if args.ctx.no_engine_commands().await {
|
||||
return Ok(helix_result);
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ async fn inner_mirror_2d(
|
||||
SketchSet::Sketches(sketches) => sketches,
|
||||
};
|
||||
|
||||
if args.ctx.no_engine_commands() {
|
||||
if args.ctx.no_engine_commands().await {
|
||||
return Ok(starting_sketches);
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
@ -0,0 +1,556 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Artifact commands assembly_non_default_units.kcl
|
||||
---
|
||||
[
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "plane_set_color",
|
||||
"plane_id": "[uuid]",
|
||||
"color": {
|
||||
"r": 0.7,
|
||||
"g": 0.28,
|
||||
"b": 0.28,
|
||||
"a": 0.4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "plane_set_color",
|
||||
"plane_id": "[uuid]",
|
||||
"color": {
|
||||
"r": 0.28,
|
||||
"g": 0.7,
|
||||
"b": 0.28,
|
||||
"a": 0.4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "plane_set_color",
|
||||
"plane_id": "[uuid]",
|
||||
"color": {
|
||||
"r": 0.28,
|
||||
"g": 0.28,
|
||||
"b": 0.7,
|
||||
"a": 0.4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": -1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 0.0,
|
||||
"y": -1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": -1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "edge_lines_visible",
|
||||
"hidden": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "set_scene_units",
|
||||
"unit": "mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
33,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "set_scene_units",
|
||||
"unit": "in"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
33,
|
||||
1
|
||||
],
|
||||
"command": {
|
||||
"type": "set_scene_units",
|
||||
"unit": "in"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
172,
|
||||
191,
|
||||
1
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 60.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
197,
|
||||
239,
|
||||
1
|
||||
],
|
||||
"command": {
|
||||
"type": "enable_sketch_mode",
|
||||
"entity_id": "[uuid]",
|
||||
"ortho": false,
|
||||
"animated": false,
|
||||
"adjust_camera": false,
|
||||
"planar_normal": {
|
||||
"x": 0.0,
|
||||
"y": -1.0,
|
||||
"z": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
197,
|
||||
239,
|
||||
1
|
||||
],
|
||||
"command": {
|
||||
"type": "start_path"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
197,
|
||||
239,
|
||||
1
|
||||
],
|
||||
"command": {
|
||||
"type": "move_path_pen",
|
||||
"path": "[uuid]",
|
||||
"to": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
197,
|
||||
239,
|
||||
1
|
||||
],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "arc",
|
||||
"center": {
|
||||
"x": 0.0,
|
||||
"y": 0.0
|
||||
},
|
||||
"radius": 1.0,
|
||||
"start": {
|
||||
"unit": "degrees",
|
||||
"value": 0.0
|
||||
},
|
||||
"end": {
|
||||
"unit": "degrees",
|
||||
"value": 360.0
|
||||
},
|
||||
"relative": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
197,
|
||||
239,
|
||||
1
|
||||
],
|
||||
"command": {
|
||||
"type": "close_path",
|
||||
"path_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
33,
|
||||
2
|
||||
],
|
||||
"command": {
|
||||
"type": "set_scene_units",
|
||||
"unit": "in"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
89,
|
||||
108,
|
||||
2
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 60.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
114,
|
||||
156,
|
||||
2
|
||||
],
|
||||
"command": {
|
||||
"type": "enable_sketch_mode",
|
||||
"entity_id": "[uuid]",
|
||||
"ortho": false,
|
||||
"animated": false,
|
||||
"adjust_camera": false,
|
||||
"planar_normal": {
|
||||
"x": 0.0,
|
||||
"y": -1.0,
|
||||
"z": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
114,
|
||||
156,
|
||||
2
|
||||
],
|
||||
"command": {
|
||||
"type": "start_path"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
114,
|
||||
156,
|
||||
2
|
||||
],
|
||||
"command": {
|
||||
"type": "move_path_pen",
|
||||
"path": "[uuid]",
|
||||
"to": {
|
||||
"x": 1.0,
|
||||
"y": 2.0,
|
||||
"z": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
114,
|
||||
156,
|
||||
2
|
||||
],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "arc",
|
||||
"center": {
|
||||
"x": 0.0,
|
||||
"y": 2.0
|
||||
},
|
||||
"radius": 1.0,
|
||||
"start": {
|
||||
"unit": "degrees",
|
||||
"value": 0.0
|
||||
},
|
||||
"end": {
|
||||
"unit": "degrees",
|
||||
"value": 360.0
|
||||
},
|
||||
"relative": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
114,
|
||||
156,
|
||||
2
|
||||
],
|
||||
"command": {
|
||||
"type": "close_path",
|
||||
"path_id": "[uuid]"
|
||||
}
|
||||
}
|
||||
]
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Artifact graph flowchart assembly_non_default_units.kcl
|
||||
extension: md
|
||||
snapshot_kind: binary
|
||||
---
|
@ -0,0 +1,21 @@
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph path2 [Path]
|
||||
2["Path<br>[197, 239, 1]"]
|
||||
3["Segment<br>[197, 239, 1]"]
|
||||
4[Solid2d]
|
||||
end
|
||||
subgraph path6 [Path]
|
||||
6["Path<br>[114, 156, 2]"]
|
||||
7["Segment<br>[114, 156, 2]"]
|
||||
8[Solid2d]
|
||||
end
|
||||
1["Plane<br>[172, 191, 1]"]
|
||||
5["Plane<br>[89, 108, 2]"]
|
||||
1 --- 2
|
||||
2 --- 3
|
||||
2 --- 4
|
||||
5 --- 6
|
||||
6 --- 7
|
||||
6 --- 8
|
||||
```
|
143
src/wasm-lib/kcl/tests/assembly_non_default_units/ast.snap
Normal file
@ -0,0 +1,143 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Result of parsing assembly_non_default_units.kcl
|
||||
---
|
||||
{
|
||||
"Ok": {
|
||||
"body": [
|
||||
{
|
||||
"end": 172,
|
||||
"path": {
|
||||
"type": "Kcl",
|
||||
"filename": "other1.kcl"
|
||||
},
|
||||
"selector": {
|
||||
"type": "None",
|
||||
"alias": null
|
||||
},
|
||||
"start": 153,
|
||||
"type": "ImportStatement",
|
||||
"type": "ImportStatement"
|
||||
},
|
||||
{
|
||||
"end": 192,
|
||||
"path": {
|
||||
"type": "Kcl",
|
||||
"filename": "other2.kcl"
|
||||
},
|
||||
"selector": {
|
||||
"type": "None",
|
||||
"alias": null
|
||||
},
|
||||
"start": 173,
|
||||
"type": "ImportStatement",
|
||||
"type": "ImportStatement"
|
||||
},
|
||||
{
|
||||
"end": 200,
|
||||
"expression": {
|
||||
"end": 200,
|
||||
"name": "other1",
|
||||
"start": 194,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 194,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
},
|
||||
{
|
||||
"end": 207,
|
||||
"expression": {
|
||||
"end": 207,
|
||||
"name": "other2",
|
||||
"start": 201,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 201,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 208,
|
||||
"innerAttrs": [
|
||||
{
|
||||
"end": 33,
|
||||
"name": {
|
||||
"end": 9,
|
||||
"name": "settings",
|
||||
"start": 1,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"end": 32,
|
||||
"key": {
|
||||
"end": 27,
|
||||
"name": "defaultLengthUnit",
|
||||
"start": 10,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 10,
|
||||
"type": "ObjectProperty",
|
||||
"value": {
|
||||
"end": 32,
|
||||
"name": "in",
|
||||
"start": 30,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
}
|
||||
}
|
||||
],
|
||||
"start": 0,
|
||||
"type": "Annotation"
|
||||
}
|
||||
],
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"1": [
|
||||
{
|
||||
"end": 194,
|
||||
"start": 192,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"startNodes": [
|
||||
{
|
||||
"end": 36,
|
||||
"start": 33,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
},
|
||||
{
|
||||
"end": 87,
|
||||
"start": 36,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "blockComment",
|
||||
"value": "Use a default unit that isn't the default of mm.",
|
||||
"style": "line"
|
||||
}
|
||||
},
|
||||
{
|
||||
"end": 152,
|
||||
"start": 88,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "blockComment",
|
||||
"value": "This should look like two circles barely touching each other.",
|
||||
"style": "line"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"start": 0
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
export radius = 1
|
10
src/wasm-lib/kcl/tests/assembly_non_default_units/input.kcl
Normal file
@ -0,0 +1,10 @@
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
|
||||
// Use a default unit that isn't the default of mm.
|
||||
// This should look like two circles barely touching each other.
|
||||
import "other1.kcl"
|
||||
import "other2.kcl"
|
||||
|
||||
other1
|
||||
other2
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Operations executed assembly_non_default_units.kcl
|
||||
---
|
||||
[]
|
@ -0,0 +1,8 @@
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
// This is not used, but it triggers the problem.
|
||||
import radius from "globals.kcl"
|
||||
|
||||
// Use the same units as in the main importing file.
|
||||
startSketchOn('XZ')
|
||||
|> circle({ center = [0, 0], radius = 1 }, %)
|
@ -0,0 +1,6 @@
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
|
||||
// Use the same units as in the main importing file.
|
||||
startSketchOn('XZ')
|
||||
|> circle({ center = [0, 2], radius = 1 }, %)
|
@ -0,0 +1,64 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Variables in memory after executing assembly_non_default_units.kcl
|
||||
---
|
||||
{
|
||||
"HALF_TURN": {
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"ty": {
|
||||
"type": "Unknown"
|
||||
},
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"ty": {
|
||||
"type": "Unknown"
|
||||
},
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"ty": {
|
||||
"type": "Unknown"
|
||||
},
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"ty": {
|
||||
"type": "Unknown"
|
||||
},
|
||||
"__meta": []
|
||||
},
|
||||
"other1": {
|
||||
"type": "Module",
|
||||
"value": 1,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
153,
|
||||
172,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"other2": {
|
||||
"type": "Module",
|
||||
"value": 2,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
173,
|
||||
192,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 133 KiB |
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB |