Parallelize the artifact graph only time suck (#6482)
* parallelize the artifact only time suck Signed-off-by: Jess Frazelle <github@jessfraz.com> updates Signed-off-by: Jess Frazelle <github@jessfraz.com> make wasm safe Signed-off-by: Jess Frazelle <github@jessfraz.com> updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * artifact graph things Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
9
rust/kcl-lib/src/engine/async_tasks/mod.rs
Normal file
9
rust/kcl-lib/src/engine/async_tasks/mod.rs
Normal file
@ -0,0 +1,9 @@
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod tasks;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod tasks_wasm;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use tasks::AsyncTasks;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use tasks_wasm::AsyncTasks;
|
51
rust/kcl-lib/src/engine/async_tasks/tasks.rs
Normal file
51
rust/kcl-lib/src/engine/async_tasks/tasks.rs
Normal file
@ -0,0 +1,51 @@
|
||||
//! This module contains the `AsyncTasks` struct, which is used to manage a set of asynchronous
|
||||
//! tasks.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::errors::KclError;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AsyncTasks {
|
||||
pub tasks: Arc<RwLock<tokio::task::JoinSet<anyhow::Result<(), KclError>>>>,
|
||||
}
|
||||
|
||||
impl AsyncTasks {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tasks: Arc::new(RwLock::new(tokio::task::JoinSet::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn spawn<F>(&mut self, task: F)
|
||||
where
|
||||
F: std::future::Future<Output = anyhow::Result<(), KclError>>,
|
||||
F: Send + 'static,
|
||||
{
|
||||
self.tasks.write().await.spawn(task);
|
||||
}
|
||||
|
||||
// Wait for all tasks to finish.
|
||||
// Return an error if any of them failed.
|
||||
pub async fn join_all(&mut self) -> anyhow::Result<(), KclError> {
|
||||
let tasks = std::mem::take(&mut *self.tasks.write().await);
|
||||
let results = tasks.join_all().await;
|
||||
for result in results {
|
||||
result?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn clear(&mut self) {
|
||||
*self.tasks.write().await = tokio::task::JoinSet::new();
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AsyncTasks {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
89
rust/kcl-lib/src/engine/async_tasks/tasks_wasm.rs
Normal file
89
rust/kcl-lib/src/engine/async_tasks/tasks_wasm.rs
Normal file
@ -0,0 +1,89 @@
|
||||
//! This module contains the `AsyncTasks` struct, which is used to manage a set of asynchronous
|
||||
//! tasks.
|
||||
|
||||
use std::{ops::AddAssign, sync::Arc};
|
||||
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::errors::KclError;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AsyncTasks {
|
||||
pub sender: Arc<RwLock<tokio::sync::mpsc::Sender<Result<(), KclError>>>>,
|
||||
pub receiver: Arc<RwLock<tokio::sync::mpsc::Receiver<Result<(), KclError>>>>,
|
||||
pub sent: Arc<RwLock<usize>>,
|
||||
}
|
||||
|
||||
impl AsyncTasks {
|
||||
pub fn new() -> Self {
|
||||
let (results_tx, results_rx) = tokio::sync::mpsc::channel(1);
|
||||
Self {
|
||||
sender: Arc::new(RwLock::new(results_tx)),
|
||||
receiver: Arc::new(RwLock::new(results_rx)),
|
||||
sent: Arc::new(RwLock::new(0)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn spawn<F>(&mut self, task: F)
|
||||
where
|
||||
F: std::future::Future<Output = anyhow::Result<(), KclError>>,
|
||||
F: Send + 'static,
|
||||
{
|
||||
// Add one to the sent counter.
|
||||
self.sent.write().await.add_assign(1);
|
||||
|
||||
// Spawn the task and send the result to the channel.
|
||||
let sender_clone = self.sender.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
let result = task.await;
|
||||
let sender = sender_clone.read().await;
|
||||
if let Err(_) = sender.send(result).await {
|
||||
web_sys::console::error_1(&"Failed to send result".into());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Wait for all tasks to finish.
|
||||
// Return an error if any of them failed.
|
||||
pub async fn join_all(&mut self) -> anyhow::Result<(), KclError> {
|
||||
if *self.sent.read().await == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut results = Vec::new();
|
||||
let mut receiver = self.receiver.write().await;
|
||||
|
||||
// Wait for all tasks to finish.
|
||||
while let Some(result) = receiver.recv().await {
|
||||
results.push(result);
|
||||
|
||||
// Check if all tasks have finished.
|
||||
if results.len() == *self.sent.read().await {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any of the tasks failed.
|
||||
for result in results {
|
||||
result?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn clear(&mut self) {
|
||||
// Clear the sent counter.
|
||||
*self.sent.write().await = 0;
|
||||
|
||||
// Clear the channel.
|
||||
let (results_tx, results_rx) = tokio::sync::mpsc::channel(1);
|
||||
*self.sender.write().await = results_tx;
|
||||
*self.receiver.write().await = results_rx;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AsyncTasks {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
@ -18,11 +18,12 @@ use tokio::sync::{mpsc, oneshot, RwLock};
|
||||
use tokio_tungstenite::tungstenite::Message as WsMsg;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::EngineStats;
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::execution::ArtifactCommand;
|
||||
use crate::{
|
||||
engine::EngineManager,
|
||||
engine::{AsyncTasks, EngineManager, EngineStats},
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{ArtifactCommand, DefaultPlanes, IdGenerator},
|
||||
execution::{DefaultPlanes, IdGenerator},
|
||||
SourceRange,
|
||||
};
|
||||
|
||||
@ -37,13 +38,14 @@ type WebSocketTcpWrite = futures::stream::SplitSink<tokio_tungstenite::WebSocket
|
||||
pub struct EngineConnection {
|
||||
engine_req_tx: mpsc::Sender<ToEngineReq>,
|
||||
shutdown_tx: mpsc::Sender<()>,
|
||||
responses: Arc<RwLock<IndexMap<uuid::Uuid, WebSocketResponse>>>,
|
||||
responses: ResponseInformation,
|
||||
pending_errors: Arc<RwLock<Vec<String>>>,
|
||||
#[allow(dead_code)]
|
||||
tcp_read_handle: Arc<TcpReadHandle>,
|
||||
socket_health: Arc<RwLock<SocketHealth>>,
|
||||
batch: Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>>,
|
||||
batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>,
|
||||
ids_of_async_commands: Arc<RwLock<IndexMap<Uuid, SourceRange>>>,
|
||||
|
||||
@ -53,6 +55,8 @@ pub struct EngineConnection {
|
||||
session_data: Arc<RwLock<Option<ModelingSessionData>>>,
|
||||
|
||||
stats: EngineStats,
|
||||
|
||||
async_tasks: AsyncTasks,
|
||||
}
|
||||
|
||||
pub struct TcpRead {
|
||||
@ -116,12 +120,14 @@ impl Drop for TcpReadHandle {
|
||||
}
|
||||
}
|
||||
|
||||
struct ResponsesInformation {
|
||||
/// Information about the responses from the engine.
|
||||
#[derive(Clone, Debug)]
|
||||
struct ResponseInformation {
|
||||
/// The responses from the engine.
|
||||
responses: Arc<RwLock<IndexMap<uuid::Uuid, WebSocketResponse>>>,
|
||||
}
|
||||
|
||||
impl ResponsesInformation {
|
||||
impl ResponseInformation {
|
||||
pub async fn add(&self, id: Uuid, response: WebSocketResponse) {
|
||||
self.responses.write().await.insert(id, response);
|
||||
}
|
||||
@ -238,14 +244,14 @@ impl EngineConnection {
|
||||
|
||||
let session_data: Arc<RwLock<Option<ModelingSessionData>>> = Arc::new(RwLock::new(None));
|
||||
let session_data2 = session_data.clone();
|
||||
let responses: Arc<RwLock<IndexMap<uuid::Uuid, WebSocketResponse>>> = Arc::new(RwLock::new(IndexMap::new()));
|
||||
let ids_of_async_commands: Arc<RwLock<IndexMap<Uuid, SourceRange>>> = Arc::new(RwLock::new(IndexMap::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 responses_information = ResponsesInformation {
|
||||
responses: responses.clone(),
|
||||
let response_information = ResponseInformation {
|
||||
responses: Arc::new(RwLock::new(IndexMap::new())),
|
||||
};
|
||||
let response_information_cloned = response_information.clone();
|
||||
|
||||
let socket_health_tcp_read = socket_health.clone();
|
||||
let tcp_read_handle = tokio::spawn(async move {
|
||||
@ -270,7 +276,7 @@ impl EngineConnection {
|
||||
BatchResponse::Success { response } => {
|
||||
// If the id is in our ids of async commands, remove
|
||||
// it.
|
||||
responses_information
|
||||
response_information_cloned
|
||||
.add(
|
||||
id,
|
||||
WebSocketResponse::Success(SuccessWebSocketResponse {
|
||||
@ -284,7 +290,7 @@ impl EngineConnection {
|
||||
.await;
|
||||
}
|
||||
BatchResponse::Failure { errors } => {
|
||||
responses_information
|
||||
response_information_cloned
|
||||
.add(
|
||||
id,
|
||||
WebSocketResponse::Failure(FailureWebSocketResponse {
|
||||
@ -311,7 +317,7 @@ impl EngineConnection {
|
||||
errors,
|
||||
}) => {
|
||||
if let Some(id) = request_id {
|
||||
responses_information
|
||||
response_information_cloned
|
||||
.add(
|
||||
*id,
|
||||
WebSocketResponse::Failure(FailureWebSocketResponse {
|
||||
@ -336,7 +342,7 @@ impl EngineConnection {
|
||||
}
|
||||
|
||||
if let Some(id) = id {
|
||||
responses_information.add(id, ws_resp.clone()).await;
|
||||
response_information_cloned.add(id, ws_resp.clone()).await;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
@ -357,16 +363,18 @@ impl EngineConnection {
|
||||
tcp_read_handle: Arc::new(TcpReadHandle {
|
||||
handle: Arc::new(tcp_read_handle),
|
||||
}),
|
||||
responses,
|
||||
responses: response_information,
|
||||
pending_errors,
|
||||
socket_health,
|
||||
batch: Arc::new(RwLock::new(Vec::new())),
|
||||
batch_end: Arc::new(RwLock::new(IndexMap::new())),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_commands: Arc::new(RwLock::new(Vec::new())),
|
||||
ids_of_async_commands,
|
||||
default_planes: Default::default(),
|
||||
session_data,
|
||||
stats: Default::default(),
|
||||
async_tasks: AsyncTasks::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -382,9 +390,10 @@ impl EngineManager for EngineConnection {
|
||||
}
|
||||
|
||||
fn responses(&self) -> Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>> {
|
||||
self.responses.clone()
|
||||
self.responses.responses.clone()
|
||||
}
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
fn artifact_commands(&self) -> Arc<RwLock<Vec<ArtifactCommand>>> {
|
||||
self.artifact_commands.clone()
|
||||
}
|
||||
@ -393,6 +402,10 @@ impl EngineManager for EngineConnection {
|
||||
self.ids_of_async_commands.clone()
|
||||
}
|
||||
|
||||
fn async_tasks(&self) -> AsyncTasks {
|
||||
self.async_tasks.clone()
|
||||
}
|
||||
|
||||
fn stats(&self) -> &EngineStats {
|
||||
&self.stats
|
||||
}
|
||||
@ -483,9 +496,19 @@ impl EngineManager for EngineConnection {
|
||||
}));
|
||||
}
|
||||
}
|
||||
// We pop off the responses to cleanup our mappings.
|
||||
if let Some(resp) = self.responses.write().await.shift_remove(&id) {
|
||||
return Ok(resp);
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
{
|
||||
// We cannot pop here or it will break the artifact graph.
|
||||
if let Some(resp) = self.responses.responses.read().await.get(&id) {
|
||||
return Ok(resp.clone());
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "artifact-graph"))]
|
||||
{
|
||||
if let Some(resp) = self.responses.responses.write().await.shift_remove(&id) {
|
||||
return Ok(resp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,11 +16,13 @@ use kittycad_modeling_cmds::{self as kcmc, websocket::ModelingCmdReq, ImportFile
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::EngineStats;
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::execution::ArtifactCommand;
|
||||
use crate::{
|
||||
engine::{AsyncTasks, EngineStats},
|
||||
errors::KclError,
|
||||
exec::DefaultPlanes,
|
||||
execution::{ArtifactCommand, IdGenerator},
|
||||
execution::IdGenerator,
|
||||
SourceRange,
|
||||
};
|
||||
|
||||
@ -28,12 +30,14 @@ use crate::{
|
||||
pub struct EngineConnection {
|
||||
batch: Arc<RwLock<Vec<(WebSocketRequest, SourceRange)>>>,
|
||||
batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>,
|
||||
ids_of_async_commands: Arc<RwLock<IndexMap<Uuid, SourceRange>>>,
|
||||
responses: Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>>,
|
||||
/// The default planes for the scene.
|
||||
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
||||
stats: EngineStats,
|
||||
async_tasks: AsyncTasks,
|
||||
}
|
||||
|
||||
impl EngineConnection {
|
||||
@ -41,11 +45,13 @@ impl EngineConnection {
|
||||
Ok(EngineConnection {
|
||||
batch: Arc::new(RwLock::new(Vec::new())),
|
||||
batch_end: Arc::new(RwLock::new(IndexMap::new())),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_commands: Arc::new(RwLock::new(Vec::new())),
|
||||
ids_of_async_commands: Arc::new(RwLock::new(IndexMap::new())),
|
||||
responses: Arc::new(RwLock::new(IndexMap::new())),
|
||||
default_planes: Default::default(),
|
||||
stats: Default::default(),
|
||||
async_tasks: AsyncTasks::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -68,6 +74,7 @@ impl crate::engine::EngineManager for EngineConnection {
|
||||
&self.stats
|
||||
}
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
fn artifact_commands(&self) -> Arc<RwLock<Vec<ArtifactCommand>>> {
|
||||
self.artifact_commands.clone()
|
||||
}
|
||||
@ -76,6 +83,10 @@ impl crate::engine::EngineManager for EngineConnection {
|
||||
self.ids_of_async_commands.clone()
|
||||
}
|
||||
|
||||
fn async_tasks(&self) -> AsyncTasks {
|
||||
self.async_tasks.clone()
|
||||
}
|
||||
|
||||
fn get_default_planes(&self) -> Arc<RwLock<Option<DefaultPlanes>>> {
|
||||
self.default_planes.clone()
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ use uuid::Uuid;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::{
|
||||
engine::EngineStats,
|
||||
engine::{AsyncTasks, EngineStats},
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{ArtifactCommand, DefaultPlanes, IdGenerator},
|
||||
SourceRange,
|
||||
@ -55,6 +55,7 @@ pub struct EngineConnection {
|
||||
/// The default planes for the scene.
|
||||
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
||||
stats: EngineStats,
|
||||
async_tasks: AsyncTasks,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
@ -128,6 +129,7 @@ impl EngineConnection {
|
||||
ids_of_async_commands: Arc::new(RwLock::new(IndexMap::new())),
|
||||
default_planes: Default::default(),
|
||||
stats: Default::default(),
|
||||
async_tasks: AsyncTasks::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -270,6 +272,10 @@ impl crate::engine::EngineManager for EngineConnection {
|
||||
self.ids_of_async_commands.clone()
|
||||
}
|
||||
|
||||
fn async_tasks(&self) -> AsyncTasks {
|
||||
self.async_tasks.clone()
|
||||
}
|
||||
|
||||
fn get_default_planes(&self) -> Arc<RwLock<Option<DefaultPlanes>>> {
|
||||
self.default_planes.clone()
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! Functions for managing engine communications.
|
||||
|
||||
pub mod async_tasks;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg(feature = "engine")]
|
||||
pub mod conn;
|
||||
@ -16,10 +17,12 @@ use std::{
|
||||
},
|
||||
};
|
||||
|
||||
pub use async_tasks::AsyncTasks;
|
||||
use indexmap::IndexMap;
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use kcmc::id::ModelingCmdId;
|
||||
use kcmc::{
|
||||
each_cmd as mcmd,
|
||||
id::ModelingCmdId,
|
||||
length_unit::LengthUnit,
|
||||
ok_response::OkModelingCmdResponse,
|
||||
shared::Color,
|
||||
@ -35,9 +38,11 @@ use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::execution::ArtifactCommand;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{types::UnitLen, ArtifactCommand, DefaultPlanes, IdGenerator, Point3d},
|
||||
execution::{types::UnitLen, DefaultPlanes, IdGenerator, Point3d},
|
||||
SourceRange,
|
||||
};
|
||||
|
||||
@ -74,11 +79,15 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
fn responses(&self) -> Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>>;
|
||||
|
||||
/// Get the artifact commands that have accumulated so far.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
fn artifact_commands(&self) -> Arc<RwLock<Vec<ArtifactCommand>>>;
|
||||
|
||||
/// Get the ids of the async commands we are waiting for.
|
||||
fn ids_of_async_commands(&self) -> Arc<RwLock<IndexMap<Uuid, SourceRange>>>;
|
||||
|
||||
/// Get the async tasks we are waiting for.
|
||||
fn async_tasks(&self) -> AsyncTasks;
|
||||
|
||||
/// Take the batch of commands that have accumulated so far and clear them.
|
||||
async fn take_batch(&self) -> Vec<(WebSocketRequest, SourceRange)> {
|
||||
std::mem::take(&mut *self.batch().write().await)
|
||||
@ -90,11 +99,13 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
}
|
||||
|
||||
/// Clear all artifact commands that have accumulated so far.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
async fn clear_artifact_commands(&self) {
|
||||
self.artifact_commands().write().await.clear();
|
||||
}
|
||||
|
||||
/// Take the artifact commands that have accumulated so far and clear them.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
async fn take_artifact_commands(&self) -> Vec<ArtifactCommand> {
|
||||
std::mem::take(&mut *self.artifact_commands().write().await)
|
||||
}
|
||||
@ -145,6 +156,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
self.batch().write().await.clear();
|
||||
self.batch_end().write().await.clear();
|
||||
self.ids_of_async_commands().write().await.clear();
|
||||
self.async_tasks().clear().await;
|
||||
}
|
||||
|
||||
/// Send a modeling command and do not wait for the response message.
|
||||
@ -186,6 +198,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
|
||||
// Ensure artifact commands are cleared so that we don't accumulate them
|
||||
// across runs.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
self.clear_artifact_commands().await;
|
||||
|
||||
// Do the after clear scene hook.
|
||||
@ -253,6 +266,18 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
self.ensure_async_command_completed(id, Some(source_range)).await?;
|
||||
}
|
||||
|
||||
// Make sure we check for all async tasks as well.
|
||||
// The reason why we ignore the error here is that, if a model fillets an edge
|
||||
// we previously called something on, it might no longer exist. In which case,
|
||||
// the artifact graph won't care either if its gone since you can't select it
|
||||
// anymore anyways.
|
||||
if let Err(err) = self.async_tasks().join_all().await {
|
||||
crate::log::logln!("Error waiting for async tasks (this is typically fine and just means that an edge became something else): {:?}", err);
|
||||
}
|
||||
|
||||
// Flush the batch to make sure nothing remains.
|
||||
self.flush_batch(true, SourceRange::default()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -273,6 +298,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
async fn handle_artifact_command(
|
||||
&self,
|
||||
cmd: &ModelingCmd,
|
||||
@ -413,6 +439,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
self.ids_of_async_commands().write().await.insert(id, source_range);
|
||||
|
||||
// Add to artifact commands.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
self.handle_artifact_command(cmd, id.into(), &HashMap::from([(id, source_range)]))
|
||||
.await?;
|
||||
|
||||
@ -486,6 +513,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
}
|
||||
|
||||
// Do the artifact commands.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
for (req, _) in orig_requests.iter() {
|
||||
match &req {
|
||||
WebSocketRequest::ModelingCmdBatchReq(ModelingBatch { requests, .. }) => {
|
||||
|
@ -3,8 +3,10 @@ use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::execution::{ArtifactCommand, ArtifactGraph, Operation};
|
||||
use crate::{
|
||||
execution::{ArtifactCommand, ArtifactGraph, DefaultPlanes, Operation},
|
||||
execution::DefaultPlanes,
|
||||
lsp::IntoDiagnostic,
|
||||
modules::{ModulePath, ModuleSource},
|
||||
source_range::SourceRange,
|
||||
@ -126,8 +128,11 @@ impl From<KclErrorWithOutputs> for KclError {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct KclErrorWithOutputs {
|
||||
pub error: KclError,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub operations: Vec<Operation>,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifact_commands: Vec<ArtifactCommand>,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifact_graph: ArtifactGraph,
|
||||
pub filenames: IndexMap<ModuleId, ModulePath>,
|
||||
pub source_files: IndexMap<ModuleId, ModuleSource>,
|
||||
@ -137,17 +142,20 @@ pub struct KclErrorWithOutputs {
|
||||
impl KclErrorWithOutputs {
|
||||
pub fn new(
|
||||
error: KclError,
|
||||
operations: Vec<Operation>,
|
||||
artifact_commands: Vec<ArtifactCommand>,
|
||||
artifact_graph: ArtifactGraph,
|
||||
#[cfg(feature = "artifact-graph")] operations: Vec<Operation>,
|
||||
#[cfg(feature = "artifact-graph")] artifact_commands: Vec<ArtifactCommand>,
|
||||
#[cfg(feature = "artifact-graph")] artifact_graph: ArtifactGraph,
|
||||
filenames: IndexMap<ModuleId, ModulePath>,
|
||||
source_files: IndexMap<ModuleId, ModuleSource>,
|
||||
default_planes: Option<DefaultPlanes>,
|
||||
) -> Self {
|
||||
Self {
|
||||
error,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
operations,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_commands,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_graph,
|
||||
filenames,
|
||||
source_files,
|
||||
@ -157,8 +165,11 @@ impl KclErrorWithOutputs {
|
||||
pub fn no_outputs(error: KclError) -> Self {
|
||||
Self {
|
||||
error,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
operations: Default::default(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_commands: Default::default(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_graph: Default::default(),
|
||||
filenames: Default::default(),
|
||||
source_files: Default::default(),
|
||||
|
@ -3,23 +3,21 @@ use std::collections::HashMap;
|
||||
use async_recursion::async_recursion;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use super::{cad_op::Group, kcl_value::TypeDef, types::PrimitiveType};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
annotations,
|
||||
cad_op::{OpArg, OpKclValue, Operation},
|
||||
kcl_value::FunctionSource,
|
||||
kcl_value::{FunctionSource, TypeDef},
|
||||
memory,
|
||||
state::ModuleState,
|
||||
types::{NumericType, RuntimeType},
|
||||
types::{NumericType, PrimitiveType, RuntimeType},
|
||||
BodyType, EnvironmentRef, ExecState, ExecutorContext, KclValue, Metadata, PlaneType, TagEngineInfo,
|
||||
TagIdentifier,
|
||||
},
|
||||
modules::{ModuleId, ModulePath, ModuleRepr},
|
||||
parsing::ast::types::{
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
|
||||
BoxNode, CallExpression, CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector,
|
||||
CallExpression, CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector,
|
||||
ItemVisibility, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name, Node, NodeRef,
|
||||
ObjectExpression, PipeExpression, Program, TagDeclarator, Type, UnaryExpression, UnaryOperator,
|
||||
},
|
||||
@ -30,6 +28,11 @@ use crate::{
|
||||
},
|
||||
CompilationError,
|
||||
};
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::{
|
||||
execution::cad_op::{Group, OpArg, OpKclValue, Operation},
|
||||
parsing::ast::types::BoxNode,
|
||||
};
|
||||
|
||||
enum StatementKind<'a> {
|
||||
Declaration { name: &'a str },
|
||||
@ -516,10 +519,11 @@ impl ExecutorContext {
|
||||
async fn exec_module_for_result(
|
||||
&self,
|
||||
module_id: ModuleId,
|
||||
module_name: &BoxNode<Name>,
|
||||
#[cfg(feature = "artifact-graph")] module_name: &BoxNode<Name>,
|
||||
exec_state: &mut ExecState,
|
||||
source_range: SourceRange,
|
||||
) -> Result<Option<KclValue>, KclError> {
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.operations.push(Operation::GroupBegin {
|
||||
group: Group::ModuleInstance {
|
||||
name: module_name.to_string(),
|
||||
@ -566,6 +570,7 @@ impl ExecutorContext {
|
||||
|
||||
exec_state.global.module_infos[&module_id].restore_repr(repr);
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.operations.push(Operation::GroupEnd);
|
||||
|
||||
result
|
||||
@ -619,8 +624,12 @@ impl ExecutorContext {
|
||||
Expr::Name(name) => {
|
||||
let value = name.get_result(exec_state, self).await?.clone();
|
||||
if let KclValue::Module { value: module_id, meta } = value {
|
||||
self.exec_module_for_result(module_id, name, exec_state, metadata.source_range)
|
||||
.await?
|
||||
self.exec_module_for_result(
|
||||
module_id,
|
||||
#[cfg(feature = "artifact-graph")] name,
|
||||
exec_state,
|
||||
metadata.source_range
|
||||
).await?
|
||||
.unwrap_or_else(|| {
|
||||
exec_state.warn(CompilationError::err(
|
||||
metadata.source_range,
|
||||
@ -1340,6 +1349,7 @@ impl Node<CallExpressionKw> {
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
let op = if func.feature_tree_operation() {
|
||||
let op_labeled_args = args
|
||||
.kw_args
|
||||
@ -1389,6 +1399,7 @@ impl Node<CallExpressionKw> {
|
||||
let result = func.std_lib_fn()(exec_state, args).await;
|
||||
exec_state.mut_stack().pop_env();
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
if let Some(mut op) = op {
|
||||
op.set_std_lib_call_is_error(result.is_err());
|
||||
// Track call operation. We do this after the call
|
||||
@ -1398,6 +1409,7 @@ impl Node<CallExpressionKw> {
|
||||
// so we need to build the op before the call.
|
||||
exec_state.global.operations.push(op);
|
||||
}
|
||||
|
||||
result
|
||||
}?;
|
||||
|
||||
@ -1426,6 +1438,7 @@ impl Node<CallExpressionKw> {
|
||||
e.add_source_ranges(vec![callsite])
|
||||
})?;
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
if matches!(fn_src, FunctionSource::User { .. }) {
|
||||
// Track return operation.
|
||||
exec_state.global.operations.push(Operation::GroupEnd);
|
||||
@ -1478,6 +1491,7 @@ impl Node<CallExpression> {
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
let op = if func.feature_tree_operation() {
|
||||
let op_labeled_args = func
|
||||
.args(false)
|
||||
@ -1514,6 +1528,7 @@ impl Node<CallExpression> {
|
||||
let result = func.std_lib_fn()(exec_state, args).await;
|
||||
exec_state.mut_stack().pop_env();
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
if let Some(mut op) = op {
|
||||
op.set_std_lib_call_is_error(result.is_err());
|
||||
// Track call operation. We do this after the call
|
||||
@ -1537,6 +1552,7 @@ impl Node<CallExpression> {
|
||||
let func = fn_name.get_result(exec_state, ctx).await?.clone();
|
||||
|
||||
// Track call operation.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.operations.push(Operation::GroupBegin {
|
||||
group: Group::FunctionCall {
|
||||
name: Some(fn_name.to_string()),
|
||||
@ -1576,6 +1592,7 @@ impl Node<CallExpression> {
|
||||
})?;
|
||||
|
||||
// Track return operation.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.operations.push(Operation::GroupEnd);
|
||||
|
||||
Ok(result)
|
||||
@ -1671,7 +1688,7 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
|
||||
let sketches_to_update: Vec<_> = exec_state
|
||||
.stack()
|
||||
.find_keys_in_current_env(|v| match v {
|
||||
KclValue::Sketch { value: sk } => sk.artifact_id == value.sketch.artifact_id,
|
||||
KclValue::Sketch { value: sk } => sk.original_id == value.sketch.original_id,
|
||||
_ => false,
|
||||
})
|
||||
.cloned()
|
||||
@ -2391,6 +2408,7 @@ impl FunctionSource {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
let op = if props.include_in_feature_tree {
|
||||
let op_labeled_args = args
|
||||
.kw_args
|
||||
@ -2418,6 +2436,7 @@ impl FunctionSource {
|
||||
let result = func(exec_state, args).await;
|
||||
exec_state.mut_stack().pop_env();
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
if let Some(mut op) = op {
|
||||
op.set_std_lib_call_is_error(result.is_err());
|
||||
// Track call operation. We do this after the call
|
||||
@ -2436,25 +2455,28 @@ impl FunctionSource {
|
||||
}
|
||||
FunctionSource::User { ast, memory, .. } => {
|
||||
// Track call operation.
|
||||
let op_labeled_args = args
|
||||
.kw_args
|
||||
.labeled
|
||||
.iter()
|
||||
.map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range)))
|
||||
.collect();
|
||||
exec_state.global.operations.push(Operation::GroupBegin {
|
||||
group: Group::FunctionCall {
|
||||
name: fn_name.clone(),
|
||||
function_source_range: ast.as_source_range(),
|
||||
unlabeled_arg: args
|
||||
.kw_args
|
||||
.unlabeled
|
||||
.as_ref()
|
||||
.map(|arg| OpArg::new(OpKclValue::from(&arg.value), arg.source_range)),
|
||||
labeled_args: op_labeled_args,
|
||||
},
|
||||
source_range: callsite,
|
||||
});
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
{
|
||||
let op_labeled_args = args
|
||||
.kw_args
|
||||
.labeled
|
||||
.iter()
|
||||
.map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range)))
|
||||
.collect();
|
||||
exec_state.global.operations.push(Operation::GroupBegin {
|
||||
group: Group::FunctionCall {
|
||||
name: fn_name.clone(),
|
||||
function_source_range: ast.as_source_range(),
|
||||
unlabeled_arg: args
|
||||
.kw_args
|
||||
.unlabeled
|
||||
.as_ref()
|
||||
.map(|arg| OpArg::new(OpKclValue::from(&arg.value), arg.source_range)),
|
||||
labeled_args: op_labeled_args,
|
||||
},
|
||||
source_range: callsite,
|
||||
});
|
||||
}
|
||||
|
||||
call_user_defined_function_kw(fn_name.as_deref(), args.kw_args, *memory, ast, exec_state, ctx).await
|
||||
}
|
||||
|
@ -8,11 +8,11 @@ use parse_display::{Display, FromStr};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::execution::ArtifactId;
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
execution::{
|
||||
types::NumericType, ArtifactId, ExecState, ExecutorContext, Metadata, TagEngineInfo, TagIdentifier, UnitLen,
|
||||
},
|
||||
execution::{types::NumericType, ExecState, ExecutorContext, Metadata, TagEngineInfo, TagIdentifier, UnitLen},
|
||||
parsing::ast::types::{Node, NodeRef, TagDeclarator, TagNode},
|
||||
std::{args::TyF64, sketch::PlaneData},
|
||||
};
|
||||
@ -255,6 +255,7 @@ pub struct Helix {
|
||||
/// The id of the helix.
|
||||
pub value: uuid::Uuid,
|
||||
/// The artifact ID.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifact_id: ArtifactId,
|
||||
/// Number of revolutions.
|
||||
pub revolutions: f64,
|
||||
@ -276,6 +277,7 @@ pub struct Plane {
|
||||
/// The id of the plane.
|
||||
pub id: uuid::Uuid,
|
||||
/// The artifact ID.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifact_id: ArtifactId,
|
||||
// The code for the plane either a string or custom.
|
||||
pub value: PlaneType,
|
||||
@ -455,6 +457,7 @@ impl Plane {
|
||||
match value {
|
||||
PlaneData::XY => Plane {
|
||||
id,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_id: id.into(),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0, UnitLen::Mm),
|
||||
x_axis: Point3d::new(1.0, 0.0, 0.0, UnitLen::Mm),
|
||||
@ -464,6 +467,7 @@ impl Plane {
|
||||
},
|
||||
PlaneData::NegXY => Plane {
|
||||
id,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_id: id.into(),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0, UnitLen::Mm),
|
||||
x_axis: Point3d::new(-1.0, 0.0, 0.0, UnitLen::Mm),
|
||||
@ -473,6 +477,7 @@ impl Plane {
|
||||
},
|
||||
PlaneData::XZ => Plane {
|
||||
id,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_id: id.into(),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0, UnitLen::Mm),
|
||||
x_axis: Point3d::new(1.0, 0.0, 0.0, UnitLen::Mm),
|
||||
@ -482,6 +487,7 @@ impl Plane {
|
||||
},
|
||||
PlaneData::NegXZ => Plane {
|
||||
id,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_id: id.into(),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0, UnitLen::Mm),
|
||||
x_axis: Point3d::new(-1.0, 0.0, 0.0, UnitLen::Mm),
|
||||
@ -491,6 +497,7 @@ impl Plane {
|
||||
},
|
||||
PlaneData::YZ => Plane {
|
||||
id,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_id: id.into(),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0, UnitLen::Mm),
|
||||
x_axis: Point3d::new(0.0, 1.0, 0.0, UnitLen::Mm),
|
||||
@ -500,6 +507,7 @@ impl Plane {
|
||||
},
|
||||
PlaneData::NegYZ => Plane {
|
||||
id,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_id: id.into(),
|
||||
origin: Point3d::new(0.0, 0.0, 0.0, UnitLen::Mm),
|
||||
x_axis: Point3d::new(0.0, -1.0, 0.0, UnitLen::Mm),
|
||||
@ -511,6 +519,7 @@ impl Plane {
|
||||
let id = exec_state.next_uuid();
|
||||
Plane {
|
||||
id,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_id: id.into(),
|
||||
origin,
|
||||
x_axis,
|
||||
@ -536,6 +545,7 @@ pub struct Face {
|
||||
/// The id of the face.
|
||||
pub id: uuid::Uuid,
|
||||
/// The artifact ID.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifact_id: ArtifactId,
|
||||
/// The tag of the face.
|
||||
pub value: String,
|
||||
@ -589,6 +599,7 @@ pub struct Sketch {
|
||||
pub tags: IndexMap<String, TagIdentifier>,
|
||||
/// The original id of the sketch. This stays the same even if the sketch is
|
||||
/// is sketched on face etc.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifact_id: ArtifactId,
|
||||
#[ts(skip)]
|
||||
pub original_id: uuid::Uuid,
|
||||
@ -752,6 +763,7 @@ pub struct Solid {
|
||||
/// The id of the solid.
|
||||
pub id: uuid::Uuid,
|
||||
/// The artifact ID of the solid. Unlike `id`, this doesn't change.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifact_id: ArtifactId,
|
||||
/// The extrude surfaces.
|
||||
pub value: Vec<ExtrudeSurface>,
|
||||
|
@ -262,6 +262,7 @@ impl KclValue {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub(crate) fn function_def_source_range(&self) -> Option<SourceRange> {
|
||||
let KclValue::Function {
|
||||
value: FunctionSource::User { ast, .. },
|
||||
|
@ -3,11 +3,13 @@
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub use artifact::{
|
||||
Artifact, ArtifactCommand, ArtifactGraph, ArtifactId, CodeRef, StartSketchOnFace, StartSketchOnPlane,
|
||||
};
|
||||
use cache::OldAstState;
|
||||
pub use cache::{bust_cache, clear_mem_cache};
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub use cad_op::Operation;
|
||||
pub use geometry::*;
|
||||
pub use id_generator::IdGenerator;
|
||||
@ -26,11 +28,12 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use state::{ExecState, MetaSettings};
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::execution::artifact::build_artifact_graph;
|
||||
use crate::{
|
||||
engine::EngineManager,
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
artifact::build_artifact_graph,
|
||||
cache::{CacheInformation, CacheResult},
|
||||
types::{UnitAngle, UnitLen},
|
||||
},
|
||||
@ -43,8 +46,10 @@ use crate::{
|
||||
};
|
||||
|
||||
pub(crate) mod annotations;
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
mod artifact;
|
||||
pub(crate) mod cache;
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
mod cad_op;
|
||||
mod exec_ast;
|
||||
mod geometry;
|
||||
@ -64,10 +69,13 @@ pub struct ExecOutcome {
|
||||
pub variables: IndexMap<String, KclValue>,
|
||||
/// Operations that have been performed in execution order, for display in
|
||||
/// the Feature Tree.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub operations: Vec<Operation>,
|
||||
/// Output commands to allow building the artifact graph by the caller.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifact_commands: Vec<ArtifactCommand>,
|
||||
/// Output artifact graph.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifact_graph: ArtifactGraph,
|
||||
/// Non-fatal errors and warnings.
|
||||
pub errors: Vec<CompilationError>,
|
||||
@ -567,7 +575,7 @@ impl ExecutorContext {
|
||||
|
||||
let mut mem = exec_state.stack().clone();
|
||||
let module_infos = exec_state.global.module_infos.clone();
|
||||
let outcome = exec_state.to_mock_wasm_outcome(result.0).await;
|
||||
let outcome = exec_state.to_mock_exec_outcome(result.0).await;
|
||||
|
||||
mem.squash_env(result.0);
|
||||
cache::write_old_memory((mem, module_infos)).await;
|
||||
@ -635,13 +643,13 @@ impl ExecutorContext {
|
||||
})
|
||||
.await;
|
||||
|
||||
let outcome = old_state.to_wasm_outcome(result_env).await;
|
||||
let outcome = old_state.to_exec_outcome(result_env).await;
|
||||
return Ok(outcome);
|
||||
}
|
||||
(true, program)
|
||||
}
|
||||
CacheResult::NoAction(false) => {
|
||||
let outcome = old_state.to_wasm_outcome(result_env).await;
|
||||
let outcome = old_state.to_exec_outcome(result_env).await;
|
||||
return Ok(outcome);
|
||||
}
|
||||
};
|
||||
@ -691,7 +699,7 @@ impl ExecutorContext {
|
||||
})
|
||||
.await;
|
||||
|
||||
let outcome = exec_state.to_wasm_outcome(result.0).await;
|
||||
let outcome = exec_state.to_exec_outcome(result.0).await;
|
||||
Ok(outcome)
|
||||
}
|
||||
|
||||
@ -721,6 +729,12 @@ impl ExecutorContext {
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<(EnvironmentRef, Option<ModelingSessionData>), KclErrorWithOutputs> {
|
||||
exec_state.add_root_module_contents(program);
|
||||
|
||||
#[cfg(test)]
|
||||
{
|
||||
exec_state.single_threaded = true;
|
||||
}
|
||||
|
||||
self.eval_prelude(exec_state, SourceRange::synthetic())
|
||||
.await
|
||||
.map_err(KclErrorWithOutputs::no_outputs)?;
|
||||
@ -760,6 +774,7 @@ impl ExecutorContext {
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
println!("Error: {err:?}");
|
||||
let module_id_to_module_path: IndexMap<ModuleId, ModulePath> = exec_state
|
||||
.global
|
||||
.path_to_source_id
|
||||
@ -769,8 +784,11 @@ impl ExecutorContext {
|
||||
|
||||
KclErrorWithOutputs::new(
|
||||
err,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.operations.clone(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.artifact_commands.clone(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.artifact_graph.clone(),
|
||||
module_id_to_module_path,
|
||||
exec_state.global.id_to_source.clone(),
|
||||
@ -789,8 +807,11 @@ impl ExecutorContext {
|
||||
|
||||
KclErrorWithOutputs::new(
|
||||
err,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.operations.clone(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.artifact_commands.clone(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.artifact_graph.clone(),
|
||||
module_id_to_module_path,
|
||||
exec_state.global.id_to_source.clone(),
|
||||
@ -934,8 +955,11 @@ impl ExecutorContext {
|
||||
|
||||
return Err(KclErrorWithOutputs::new(
|
||||
e,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.operations.clone(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.artifact_commands.clone(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.artifact_graph.clone(),
|
||||
module_id_to_module_path,
|
||||
exec_state.global.id_to_source.clone(),
|
||||
@ -986,8 +1010,11 @@ impl ExecutorContext {
|
||||
|
||||
KclErrorWithOutputs::new(
|
||||
e,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.operations.clone(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.artifact_commands.clone(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
exec_state.global.artifact_graph.clone(),
|
||||
module_id_to_module_path,
|
||||
exec_state.global.id_to_source.clone(),
|
||||
@ -1036,32 +1063,39 @@ impl ExecutorContext {
|
||||
// and should be dropped.
|
||||
self.engine.clear_queues().await;
|
||||
|
||||
// Move the artifact commands and responses to simplify cache management
|
||||
// and error creation.
|
||||
exec_state
|
||||
.global
|
||||
.artifact_commands
|
||||
.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,
|
||||
&exec_state.global.artifact_responses,
|
||||
program,
|
||||
&exec_state.global.artifacts,
|
||||
) {
|
||||
Ok(artifact_graph) => {
|
||||
exec_state.global.artifact_graph = artifact_graph;
|
||||
exec_result.map(|(_, env_ref, _)| env_ref)
|
||||
}
|
||||
Err(err) => {
|
||||
// Prefer the exec error.
|
||||
exec_result.and(Err(err))
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
{
|
||||
// Move the artifact commands and responses to simplify cache management
|
||||
// and error creation.
|
||||
exec_state
|
||||
.global
|
||||
.artifact_commands
|
||||
.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,
|
||||
&exec_state.global.artifact_responses,
|
||||
program,
|
||||
&exec_state.global.artifacts,
|
||||
) {
|
||||
Ok(artifact_graph) => {
|
||||
exec_state.global.artifact_graph = artifact_graph;
|
||||
exec_result.map(|(_, env_ref, _)| env_ref)
|
||||
}
|
||||
Err(err) => {
|
||||
// Prefer the exec error.
|
||||
exec_result.and(Err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "artifact-graph"))]
|
||||
{
|
||||
exec_result.map(|(_, env_ref, _)| env_ref)
|
||||
}
|
||||
}
|
||||
|
||||
/// 'Import' std::prelude as the outermost scope.
|
||||
|
@ -2,20 +2,23 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use indexmap::IndexMap;
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use kittycad_modeling_cmds::websocket::WebSocketResponse;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::types::NumericType;
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::execution::{Artifact, ArtifactCommand, ArtifactGraph, ArtifactId, Operation};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails, Severity},
|
||||
execution::{
|
||||
annotations,
|
||||
id_generator::IdGenerator,
|
||||
memory::{ProgramMemory, Stack},
|
||||
types, Artifact, ArtifactCommand, ArtifactGraph, ArtifactId, EnvironmentRef, ExecOutcome, ExecutorSettings,
|
||||
KclValue, Operation, UnitAngle, UnitLen,
|
||||
types,
|
||||
types::NumericType,
|
||||
EnvironmentRef, ExecOutcome, ExecutorSettings, KclValue, UnitAngle, UnitLen,
|
||||
},
|
||||
modules::{ModuleId, ModuleInfo, ModuleLoader, ModulePath, ModuleRepr, ModuleSource},
|
||||
parsing::ast::types::Annotation,
|
||||
@ -29,6 +32,9 @@ pub struct ExecState {
|
||||
pub(super) global: GlobalState,
|
||||
pub(super) mod_local: ModuleState,
|
||||
pub(super) exec_context: Option<super::ExecutorContext>,
|
||||
/// If we should not parallelize execution.
|
||||
#[cfg(test)]
|
||||
pub single_threaded: bool,
|
||||
}
|
||||
|
||||
pub type ModuleInfoMap = IndexMap<ModuleId, ModuleInfo>;
|
||||
@ -42,20 +48,25 @@ pub(super) struct GlobalState {
|
||||
/// Map from module ID to module info.
|
||||
pub module_infos: ModuleInfoMap,
|
||||
/// Output map of UUIDs to artifacts.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifacts: IndexMap<ArtifactId, Artifact>,
|
||||
/// Output commands to allow building the artifact graph by the caller.
|
||||
/// These are accumulated in the [`ExecutorContext`] but moved here for
|
||||
/// convenience of the execution cache.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifact_commands: Vec<ArtifactCommand>,
|
||||
/// Responses from the engine for `artifact_commands`. We need to cache
|
||||
/// this so that we can build the artifact graph. These are accumulated in
|
||||
/// the [`ExecutorContext`] but moved here for convenience of the execution
|
||||
/// cache.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifact_responses: IndexMap<Uuid, WebSocketResponse>,
|
||||
/// Output artifact graph.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifact_graph: ArtifactGraph,
|
||||
/// Operations that have been performed in execution order, for display in
|
||||
/// the Feature Tree.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub operations: Vec<Operation>,
|
||||
/// Module loader.
|
||||
pub mod_loader: ModuleLoader,
|
||||
@ -85,6 +96,8 @@ impl ExecState {
|
||||
global: GlobalState::new(&exec_context.settings),
|
||||
mod_local: ModuleState::new(None, ProgramMemory::new(), Default::default()),
|
||||
exec_context: Some(exec_context.clone()),
|
||||
#[cfg(test)]
|
||||
single_threaded: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,6 +108,8 @@ impl ExecState {
|
||||
global,
|
||||
mod_local: ModuleState::new(None, ProgramMemory::new(), Default::default()),
|
||||
exec_context: Some(exec_context.clone()),
|
||||
#[cfg(test)]
|
||||
single_threaded: false,
|
||||
};
|
||||
}
|
||||
|
||||
@ -116,7 +131,7 @@ impl ExecState {
|
||||
/// Convert to execution outcome when running in WebAssembly. We want to
|
||||
/// reduce the amount of data that crosses the WASM boundary as much as
|
||||
/// possible.
|
||||
pub async fn to_wasm_outcome(self, main_ref: EnvironmentRef) -> ExecOutcome {
|
||||
pub async fn to_exec_outcome(self, main_ref: EnvironmentRef) -> ExecOutcome {
|
||||
// Fields are opt-in so that we don't accidentally leak private internal
|
||||
// state when we add more to ExecState.
|
||||
ExecOutcome {
|
||||
@ -125,8 +140,11 @@ impl ExecState {
|
||||
.find_all_in_env(main_ref)
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
operations: self.global.operations,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_commands: self.global.artifact_commands,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_graph: self.global.artifact_graph,
|
||||
errors: self.global.errors,
|
||||
filenames: self
|
||||
@ -143,7 +161,7 @@ impl ExecState {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn to_mock_wasm_outcome(self, main_ref: EnvironmentRef) -> ExecOutcome {
|
||||
pub async fn to_mock_exec_outcome(self, main_ref: EnvironmentRef) -> ExecOutcome {
|
||||
// Fields are opt-in so that we don't accidentally leak private internal
|
||||
// state when we add more to ExecState.
|
||||
ExecOutcome {
|
||||
@ -152,8 +170,11 @@ impl ExecState {
|
||||
.find_all_in_env(main_ref)
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
operations: Default::default(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_commands: Default::default(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_graph: Default::default(),
|
||||
errors: self.global.errors,
|
||||
filenames: Default::default(),
|
||||
@ -181,6 +202,7 @@ impl ExecState {
|
||||
&mut self.mod_local.id_generator
|
||||
}
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub(crate) fn add_artifact(&mut self, artifact: Artifact) {
|
||||
let id = artifact.id();
|
||||
self.global.artifacts.insert(id, artifact);
|
||||
@ -271,10 +293,15 @@ impl GlobalState {
|
||||
let mut global = GlobalState {
|
||||
path_to_source_id: Default::default(),
|
||||
module_infos: Default::default(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifacts: Default::default(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_commands: Default::default(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_responses: Default::default(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_graph: Default::default(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
operations: Default::default(),
|
||||
mod_loader: Default::default(),
|
||||
errors: Default::default(),
|
||||
|
@ -1051,6 +1051,7 @@ impl KclValue {
|
||||
let id = exec_state.mod_local.id_generator.next_uuid();
|
||||
let plane = Plane {
|
||||
id,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_id: id.into(),
|
||||
origin,
|
||||
x_axis,
|
||||
|
@ -81,7 +81,7 @@ pub mod walk;
|
||||
mod wasm;
|
||||
|
||||
pub use coredump::CoreDump;
|
||||
pub use engine::{EngineManager, EngineStats};
|
||||
pub use engine::{AsyncTasks, EngineManager, EngineStats};
|
||||
pub use errors::{
|
||||
CompilationError, ConnectionError, ExecError, KclError, KclErrorWithOutputs, Report, ReportWithOutputs,
|
||||
};
|
||||
@ -102,7 +102,9 @@ pub use unparser::{recast_dir, walk_dir};
|
||||
// Rather than make executor public and make lots of it pub(crate), just re-export into a new module.
|
||||
// Ideally we wouldn't export these things at all, they should only be used for testing.
|
||||
pub mod exec {
|
||||
pub use crate::execution::{ArtifactCommand, DefaultPlanes, IdGenerator, KclValue, PlaneType, Sketch};
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub use crate::execution::ArtifactCommand;
|
||||
pub use crate::execution::{DefaultPlanes, IdGenerator, KclValue, PlaneType, Sketch};
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
|
@ -1,16 +1,17 @@
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use std::collections::HashMap;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
panic::{catch_unwind, AssertUnwindSafe},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use insta::rounded_redaction;
|
||||
|
||||
use crate::{errors::KclError, ModuleId};
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
exec::ArtifactCommand,
|
||||
execution::{ArtifactGraph, Operation},
|
||||
ModuleId,
|
||||
};
|
||||
|
||||
mod kcl_samples;
|
||||
@ -156,9 +157,12 @@ async fn execute_test(test: &Test, render_to_png: bool, export_step: bool) {
|
||||
let ast = crate::Program::parse_no_errs(&input).unwrap();
|
||||
|
||||
// Run the program.
|
||||
let exec_res =
|
||||
crate::test_server::execute_and_snapshot_ast(ast, Some(test.input_dir.join(&test.entry_point)), export_step)
|
||||
.await;
|
||||
let exec_res = crate::test_server::execute_and_snapshot_ast_single_threaded(
|
||||
ast,
|
||||
Some(test.input_dir.join(&test.entry_point)),
|
||||
export_step,
|
||||
)
|
||||
.await;
|
||||
match exec_res {
|
||||
Ok((exec_state, env_ref, png, step)) => {
|
||||
let fail_path = test.output_dir.join("execution_error.snap");
|
||||
@ -178,7 +182,7 @@ async fn execute_test(test: &Test, render_to_png: bool, export_step: bool) {
|
||||
panic!("Step data was empty");
|
||||
}
|
||||
}
|
||||
let outcome = exec_state.to_wasm_outcome(env_ref).await;
|
||||
let outcome = exec_state.to_exec_outcome(env_ref).await;
|
||||
|
||||
let mem_result = catch_unwind(AssertUnwindSafe(|| {
|
||||
assert_snapshot(test, "Variables in memory after executing", || {
|
||||
@ -196,6 +200,7 @@ async fn execute_test(test: &Test, render_to_png: bool, export_step: bool) {
|
||||
})
|
||||
}));
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_common_snapshots(
|
||||
test,
|
||||
outcome.operations,
|
||||
@ -230,6 +235,7 @@ async fn execute_test(test: &Test, render_to_png: bool, export_step: bool) {
|
||||
})
|
||||
}));
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_common_snapshots(test, error.operations, error.artifact_commands, error.artifact_graph);
|
||||
err_result.unwrap();
|
||||
}
|
||||
@ -246,6 +252,7 @@ async fn execute_test(test: &Test, render_to_png: bool, export_step: bool) {
|
||||
|
||||
/// Assert snapshots that should happen both when KCL execution succeeds and
|
||||
/// when it results in an error.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
fn assert_common_snapshots(
|
||||
test: &Test,
|
||||
operations: Vec<Operation>,
|
||||
@ -1560,6 +1567,7 @@ mod mike_stress_test {
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
#[ignore = "when kurt made the artifact graph lots of commands, this became super slow and sometimes the engine will just die, turn this back on when we can parallelize the simulation tests with snapshots deterministically"]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
|
@ -9,14 +9,13 @@ use rgba_simple::Hex;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::args::TyF64;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{types::RuntimeType, ExecState, KclValue, SolidOrImportedGeometry},
|
||||
std::Args,
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref HEX_REGEX: Regex = Regex::new(r"^#[0-9a-fA-F]{6}$").unwrap();
|
||||
}
|
||||
|
@ -3,14 +3,13 @@
|
||||
use anyhow::Result;
|
||||
use kcl_derive_docs::stdlib;
|
||||
|
||||
use super::args::TyF64;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{ExecState, KclValue},
|
||||
std::Args,
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
async fn _assert(value: bool, message: &str, args: &Args) -> Result<(), KclError> {
|
||||
if !value {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
|
@ -1,8 +1,7 @@
|
||||
//! Types for referencing an axis or edge.
|
||||
|
||||
use crate::std::fillet::EdgeReference;
|
||||
|
||||
use super::args::TyF64;
|
||||
use crate::std::fillet::EdgeReference;
|
||||
|
||||
/// A 2D axis or tagged edge.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
@ -5,6 +5,7 @@ use kcl_derive_docs::stdlib;
|
||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, shared::CutType, ModelingCmd};
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
|
||||
use super::args::TyF64;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
@ -15,8 +16,6 @@ use crate::{
|
||||
std::{fillet::EdgeReference, Args},
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
pub(crate) const DEFAULT_TOLERANCE: f64 = 0.0000001;
|
||||
|
||||
/// Create chamfers on tagged paths.
|
||||
|
@ -293,14 +293,20 @@ async fn inner_clone(
|
||||
let mut new_sketch = sketch.clone();
|
||||
new_sketch.id = new_id;
|
||||
new_sketch.original_id = new_id;
|
||||
new_sketch.artifact_id = new_id.into();
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
{
|
||||
new_sketch.artifact_id = new_id.into();
|
||||
}
|
||||
GeometryWithImportedGeometry::Sketch(new_sketch)
|
||||
}
|
||||
GeometryWithImportedGeometry::Solid(solid) => {
|
||||
let mut new_solid = solid.clone();
|
||||
new_solid.id = new_id;
|
||||
new_solid.sketch.original_id = new_id;
|
||||
new_solid.artifact_id = new_id.into();
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
{
|
||||
new_solid.artifact_id = new_id.into();
|
||||
}
|
||||
GeometryWithImportedGeometry::Solid(new_solid)
|
||||
}
|
||||
};
|
||||
@ -343,7 +349,10 @@ async fn fix_tags_and_references(
|
||||
// Make the sketch id the new geometry id.
|
||||
solid.sketch.id = new_geometry_id;
|
||||
solid.sketch.original_id = new_geometry_id;
|
||||
solid.sketch.artifact_id = new_geometry_id.into();
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
{
|
||||
solid.sketch.artifact_id = new_geometry_id.into();
|
||||
}
|
||||
|
||||
fix_sketch_tags_and_references(&mut solid.sketch, &entity_id_map, exec_state).await?;
|
||||
|
||||
@ -368,6 +377,7 @@ async fn fix_tags_and_references(
|
||||
// information.
|
||||
let new_solid = do_post_extrude(
|
||||
&solid.sketch,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
new_geometry_id.into(),
|
||||
crate::std::args::TyF64::new(
|
||||
solid.height,
|
||||
@ -547,8 +557,10 @@ clonedCube = clone(cube)
|
||||
|
||||
assert_ne!(cube.id, cloned_cube.id);
|
||||
assert_ne!(cube.original_id, cloned_cube.original_id);
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_ne!(cube.artifact_id, cloned_cube.artifact_id);
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_eq!(cloned_cube.artifact_id, cloned_cube.id.into());
|
||||
assert_eq!(cloned_cube.original_id, cloned_cube.id);
|
||||
|
||||
@ -597,9 +609,12 @@ clonedCube = clone(cube)
|
||||
assert_ne!(cube.id, cloned_cube.id);
|
||||
assert_ne!(cube.sketch.id, cloned_cube.sketch.id);
|
||||
assert_ne!(cube.sketch.original_id, cloned_cube.sketch.original_id);
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_ne!(cube.artifact_id, cloned_cube.artifact_id);
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_ne!(cube.sketch.artifact_id, cloned_cube.sketch.artifact_id);
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_eq!(cloned_cube.artifact_id, cloned_cube.id.into());
|
||||
|
||||
for (path, cloned_path) in cube.sketch.paths.iter().zip(cloned_cube.sketch.paths.iter()) {
|
||||
@ -711,9 +726,12 @@ clonedCube = clone(cube)
|
||||
assert_ne!(cube.id, cloned_cube.id);
|
||||
assert_ne!(cube.sketch.id, cloned_cube.sketch.id);
|
||||
assert_ne!(cube.sketch.original_id, cloned_cube.sketch.original_id);
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_ne!(cube.artifact_id, cloned_cube.artifact_id);
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_ne!(cube.sketch.artifact_id, cloned_cube.sketch.artifact_id);
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_eq!(cloned_cube.artifact_id, cloned_cube.id.into());
|
||||
|
||||
for (path, cloned_path) in cube.sketch.paths.iter().zip(cloned_cube.sketch.paths.iter()) {
|
||||
@ -783,9 +801,12 @@ clonedCube = clone(cube)
|
||||
assert_ne!(cube.id, cloned_cube.id);
|
||||
assert_ne!(cube.sketch.id, cloned_cube.sketch.id);
|
||||
assert_ne!(cube.sketch.original_id, cloned_cube.sketch.original_id);
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_ne!(cube.artifact_id, cloned_cube.artifact_id);
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_ne!(cube.sketch.artifact_id, cloned_cube.sketch.artifact_id);
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_eq!(cloned_cube.artifact_id, cloned_cube.id.into());
|
||||
|
||||
for (path, cloned_path) in cube.sketch.paths.iter().zip(cloned_cube.sketch.paths.iter()) {
|
||||
@ -883,9 +904,12 @@ clonedCube = clone(cube)
|
||||
assert_ne!(cube.id, cloned_cube.id);
|
||||
assert_ne!(cube.sketch.id, cloned_cube.sketch.id);
|
||||
assert_ne!(cube.sketch.original_id, cloned_cube.sketch.original_id);
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_ne!(cube.artifact_id, cloned_cube.artifact_id);
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_ne!(cube.sketch.artifact_id, cloned_cube.sketch.artifact_id);
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
assert_eq!(cloned_cube.artifact_id, cloned_cube.id.into());
|
||||
|
||||
for (path, cloned_path) in cube.sketch.paths.iter().zip(cloned_cube.sketch.paths.iter()) {
|
||||
|
@ -10,14 +10,13 @@ use kittycad_modeling_cmds::{
|
||||
websocket::OkWebSocketResponseData,
|
||||
};
|
||||
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{types::RuntimeType, ExecState, KclValue, Solid},
|
||||
std::Args,
|
||||
};
|
||||
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
||||
|
||||
/// Union two or more solids into a single solid.
|
||||
pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solids: Vec<Solid> =
|
||||
|
@ -16,18 +16,16 @@ use kcmc::{
|
||||
use kittycad_modeling_cmds::{self as kcmc};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::args::TyF64;
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::execution::ArtifactId;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
types::RuntimeType, ArtifactId, ExecState, ExtrudeSurface, GeoMeta, KclValue, Path, Sketch, SketchSurface,
|
||||
Solid,
|
||||
},
|
||||
execution::{types::RuntimeType, ExecState, ExtrudeSurface, GeoMeta, KclValue, Path, Sketch, SketchSurface, Solid},
|
||||
parsing::ast::types::TagNode,
|
||||
std::Args,
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
/// Extrudes by a given amount.
|
||||
pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
|
||||
@ -212,6 +210,7 @@ async fn inner_extrude(
|
||||
solids.push(
|
||||
do_post_extrude(
|
||||
sketch,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
id.into(),
|
||||
length.clone(),
|
||||
false,
|
||||
@ -237,7 +236,7 @@ pub(crate) struct NamedCapTags<'a> {
|
||||
|
||||
pub(crate) async fn do_post_extrude<'a>(
|
||||
sketch: &Sketch,
|
||||
solid_id: ArtifactId,
|
||||
#[cfg(feature = "artifact-graph")] solid_id: ArtifactId,
|
||||
length: TyF64,
|
||||
sectional: bool,
|
||||
named_cap_tags: &'a NamedCapTags<'a>,
|
||||
@ -297,6 +296,7 @@ pub(crate) async fn do_post_extrude<'a>(
|
||||
// are the ones that work with GetOppositeEdge and GetNextAdjacentEdge, aka the n sides in the sweep.
|
||||
// So here we're figuring out that n number as yielded_sides_count here,
|
||||
// making sure that circle() calls count but close() don't (no length)
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
let count_of_first_set_of_faces_if_sectional = if sectional {
|
||||
sketch
|
||||
.paths
|
||||
@ -311,6 +311,8 @@ pub(crate) async fn do_post_extrude<'a>(
|
||||
usize::MAX
|
||||
};
|
||||
|
||||
// Only do this if we need the artifact graph.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
for (curve_id, face_id) in face_infos
|
||||
.iter()
|
||||
.filter(|face_info| face_info.cap == ExtrusionFaceCapType::None)
|
||||
@ -327,28 +329,21 @@ pub(crate) async fn do_post_extrude<'a>(
|
||||
// So, there's no need to await them.
|
||||
// Instead, the Typescript codebases (which handles WebSocket sends when compiled via Wasm)
|
||||
// uses this to build the artifact graph, which the UI needs.
|
||||
let opposite_edge_id = args
|
||||
.send_modeling_cmd(
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::Solid3dGetOppositeEdge {
|
||||
edge_id: curve_id,
|
||||
object_id: sketch.id,
|
||||
face_id,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
let next_adjacent_edge_id = args
|
||||
.send_modeling_cmd(
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::Solid3dGetNextAdjacentEdge {
|
||||
edge_id: curve_id,
|
||||
object_id: sketch.id,
|
||||
face_id,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
//
|
||||
// Spawn this in the background, because we don't care about the result.
|
||||
// Only the artifact graph needs at the end.
|
||||
let args_cloned = args.clone();
|
||||
let opposite_edge_uuid = exec_state.next_uuid();
|
||||
let next_adjacent_edge_uuid = exec_state.next_uuid();
|
||||
let get_all_edge_faces_opposite_uuid = exec_state.next_uuid();
|
||||
let get_all_edge_faces_next_uuid = exec_state.next_uuid();
|
||||
#[cfg(test)]
|
||||
let single_threaded = exec_state.single_threaded;
|
||||
#[cfg(not(test))]
|
||||
let single_threaded = false;
|
||||
|
||||
// Get faces for original edge
|
||||
// Since this one is batched we can just run it.
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::Solid3dGetAllEdgeFaces {
|
||||
@ -358,36 +353,56 @@ pub(crate) async fn do_post_extrude<'a>(
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Get faces for opposite edge
|
||||
if let OkWebSocketResponseData::Modeling {
|
||||
modeling_response: OkModelingCmdResponse::Solid3dGetOppositeEdge(opposite_edge),
|
||||
} = opposite_edge_id
|
||||
{
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::Solid3dGetAllEdgeFaces {
|
||||
edge_id: opposite_edge.edge,
|
||||
object_id: sketch.id,
|
||||
}),
|
||||
if !single_threaded {
|
||||
args.ctx
|
||||
.engine
|
||||
.async_tasks()
|
||||
.spawn(get_bg_edge_info_opposite(
|
||||
args_cloned.clone(),
|
||||
curve_id,
|
||||
sketch.id,
|
||||
face_id,
|
||||
opposite_edge_uuid,
|
||||
get_all_edge_faces_opposite_uuid,
|
||||
single_threaded,
|
||||
))
|
||||
.await;
|
||||
|
||||
args.ctx
|
||||
.engine
|
||||
.async_tasks()
|
||||
.spawn(get_bg_edge_info_next(
|
||||
args_cloned,
|
||||
curve_id,
|
||||
sketch.id,
|
||||
face_id,
|
||||
next_adjacent_edge_uuid,
|
||||
get_all_edge_faces_next_uuid,
|
||||
single_threaded,
|
||||
))
|
||||
.await;
|
||||
} else {
|
||||
get_bg_edge_info_opposite(
|
||||
args_cloned.clone(),
|
||||
curve_id,
|
||||
sketch.id,
|
||||
face_id,
|
||||
opposite_edge_uuid,
|
||||
get_all_edge_faces_opposite_uuid,
|
||||
single_threaded,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
// Get faces for next adjacent edge
|
||||
if let OkWebSocketResponseData::Modeling {
|
||||
modeling_response: OkModelingCmdResponse::Solid3dGetNextAdjacentEdge(next_adjacent_edge),
|
||||
} = next_adjacent_edge_id
|
||||
{
|
||||
if let Some(edge_id) = next_adjacent_edge.edge {
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::Solid3dGetAllEdgeFaces {
|
||||
edge_id,
|
||||
object_id: sketch.id,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
get_bg_edge_info_next(
|
||||
args_cloned,
|
||||
curve_id,
|
||||
sketch.id,
|
||||
face_id,
|
||||
next_adjacent_edge_uuid,
|
||||
get_all_edge_faces_next_uuid,
|
||||
single_threaded,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -509,6 +524,7 @@ pub(crate) async fn do_post_extrude<'a>(
|
||||
// that we passed in to the function, but it's actually the id of the
|
||||
// sketch.
|
||||
id: sketch.id,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_id: solid_id,
|
||||
value: new_value,
|
||||
meta: sketch.meta.clone(),
|
||||
@ -559,3 +575,101 @@ async fn analyze_faces(exec_state: &mut ExecState, args: &Args, face_infos: Vec<
|
||||
}
|
||||
faces
|
||||
}
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
async fn send_fn(args: &Args, id: uuid::Uuid, cmd: ModelingCmd, single_threaded: bool) -> Result<(), KclError> {
|
||||
if single_threaded {
|
||||
// In single threaded mode, we can safely batch the command.
|
||||
args.batch_modeling_cmd(id, cmd).await
|
||||
} else {
|
||||
// We cannot batch this call, because otherwise it might batch after say
|
||||
// a shell that makes this edge no longer relevant.
|
||||
args.send_modeling_cmd(id, cmd).await.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn get_bg_edge_info_next(
|
||||
args: Args,
|
||||
curve_id: uuid::Uuid,
|
||||
sketch_id: uuid::Uuid,
|
||||
face_id: uuid::Uuid,
|
||||
edge_uuid: uuid::Uuid,
|
||||
get_all_edge_faces_uuid: uuid::Uuid,
|
||||
single_threaded: bool,
|
||||
) -> Result<(), KclError> {
|
||||
let next_adjacent_edge_id = args
|
||||
.send_modeling_cmd(
|
||||
edge_uuid,
|
||||
ModelingCmd::from(mcmd::Solid3dGetNextAdjacentEdge {
|
||||
edge_id: curve_id,
|
||||
object_id: sketch_id,
|
||||
face_id,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Get faces for next adjacent edge
|
||||
if let OkWebSocketResponseData::Modeling {
|
||||
modeling_response: OkModelingCmdResponse::Solid3dGetNextAdjacentEdge(next_adjacent_edge),
|
||||
} = next_adjacent_edge_id
|
||||
{
|
||||
if let Some(edge_id) = next_adjacent_edge.edge {
|
||||
send_fn(
|
||||
&args,
|
||||
get_all_edge_faces_uuid,
|
||||
ModelingCmd::from(mcmd::Solid3dGetAllEdgeFaces {
|
||||
edge_id,
|
||||
object_id: sketch_id,
|
||||
}),
|
||||
single_threaded,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn get_bg_edge_info_opposite(
|
||||
args: Args,
|
||||
curve_id: uuid::Uuid,
|
||||
sketch_id: uuid::Uuid,
|
||||
face_id: uuid::Uuid,
|
||||
edge_uuid: uuid::Uuid,
|
||||
get_all_edge_faces_uuid: uuid::Uuid,
|
||||
single_threaded: bool,
|
||||
) -> Result<(), KclError> {
|
||||
let opposite_edge_id = args
|
||||
.send_modeling_cmd(
|
||||
edge_uuid,
|
||||
ModelingCmd::from(mcmd::Solid3dGetOppositeEdge {
|
||||
edge_id: curve_id,
|
||||
object_id: sketch_id,
|
||||
face_id,
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Get faces for opposite edge
|
||||
if let OkWebSocketResponseData::Modeling {
|
||||
modeling_response: OkModelingCmdResponse::Solid3dGetOppositeEdge(opposite_edge),
|
||||
} = opposite_edge_id
|
||||
{
|
||||
send_fn(
|
||||
&args,
|
||||
get_all_edge_faces_uuid,
|
||||
ModelingCmd::from(mcmd::Solid3dGetAllEdgeFaces {
|
||||
edge_id: opposite_edge.edge,
|
||||
object_id: sketch_id,
|
||||
}),
|
||||
single_threaded,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ use anyhow::Result;
|
||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, shared::Angle, ModelingCmd};
|
||||
use kittycad_modeling_cmds::{self as kcmc, shared::Point3d};
|
||||
|
||||
use super::args::TyF64;
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
execution::{
|
||||
@ -13,8 +14,6 @@ use crate::{
|
||||
std::{axis_or_reference::Axis3dOrEdgeReference, Args},
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
/// Create a helix.
|
||||
pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let angle_start: TyF64 = args.get_kw_arg_typed("angleStart", &RuntimeType::degrees(), exec_state)?;
|
||||
@ -111,6 +110,7 @@ async fn inner_helix(
|
||||
|
||||
let helix_result = Box::new(HelixValue {
|
||||
value: id,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_id: id.into(),
|
||||
revolutions,
|
||||
angle_start,
|
||||
|
@ -176,6 +176,7 @@ async fn inner_loft(
|
||||
Ok(Box::new(
|
||||
do_post_extrude(
|
||||
&sketch,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
id.into(),
|
||||
TyF64::new(0.0, NumericType::mm()),
|
||||
false,
|
||||
|
@ -175,6 +175,7 @@ async fn inner_revolve(
|
||||
solids.push(
|
||||
do_post_extrude(
|
||||
sketch,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
id.into(),
|
||||
TyF64::new(0.0, NumericType::mm()),
|
||||
false,
|
||||
|
@ -5,14 +5,13 @@ use kcl_derive_docs::stdlib;
|
||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
|
||||
use super::args::TyF64;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{types::RuntimeType, ExecState, KclValue, Solid},
|
||||
std::{sketch::FaceTag, Args},
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
/// Create a shell.
|
||||
pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let solids = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
|
||||
|
@ -12,21 +12,22 @@ use parse_display::{Display, FromStr};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::utils::{point_to_len_unit, point_to_mm, untype_point, untyped_point_to_mm};
|
||||
use crate::execution::types::ArrayLen;
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::execution::{Artifact, ArtifactId, CodeRef, StartSketchOnFace, StartSketchOnPlane};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
types::{NumericType, PrimitiveType, RuntimeType, UnitLen},
|
||||
Artifact, ArtifactId, BasePath, CodeRef, ExecState, Face, GeoMeta, KclValue, Path, Plane, Point2d, Point3d,
|
||||
Sketch, SketchSurface, Solid, StartSketchOnFace, StartSketchOnPlane, TagEngineInfo, TagIdentifier,
|
||||
types::{ArrayLen, NumericType, PrimitiveType, RuntimeType, UnitLen},
|
||||
BasePath, ExecState, Face, GeoMeta, KclValue, Path, Plane, Point2d, Point3d, Sketch, SketchSurface, Solid,
|
||||
TagEngineInfo, TagIdentifier,
|
||||
},
|
||||
parsing::ast::types::TagNode,
|
||||
std::{
|
||||
args::{Args, TyF64},
|
||||
utils::{
|
||||
arc_center_and_end, get_tangential_arc_to_info, get_x_component, get_y_component,
|
||||
intersection_with_parallel_line, TangentialArcInfoInput,
|
||||
intersection_with_parallel_line, point_to_len_unit, point_to_mm, untype_point, untyped_point_to_mm,
|
||||
TangentialArcInfoInput,
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -981,7 +982,7 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
|
||||
/// Start a new 2-dimensional sketch on a specific plane or face.
|
||||
///
|
||||
/// ### Sketch on Face Behavior
|
||||
/// ## Sketch on Face Behavior
|
||||
///
|
||||
/// There are some important behaviors to understand when sketching on a face:
|
||||
///
|
||||
@ -1180,12 +1181,15 @@ async fn inner_start_sketch_on(
|
||||
Ok(SketchSurface::Plane(plane))
|
||||
} else {
|
||||
// Create artifact used only by the UI, not the engine.
|
||||
let id = exec_state.next_uuid();
|
||||
exec_state.add_artifact(Artifact::StartSketchOnPlane(StartSketchOnPlane {
|
||||
id: ArtifactId::from(id),
|
||||
plane_id: plane.artifact_id,
|
||||
code_ref: CodeRef::placeholder(args.source_range),
|
||||
}));
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
{
|
||||
let id = exec_state.next_uuid();
|
||||
exec_state.add_artifact(Artifact::StartSketchOnPlane(StartSketchOnPlane {
|
||||
id: ArtifactId::from(id),
|
||||
plane_id: plane.artifact_id,
|
||||
code_ref: CodeRef::placeholder(args.source_range),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(SketchSurface::Plane(plane))
|
||||
}
|
||||
@ -1199,13 +1203,16 @@ async fn inner_start_sketch_on(
|
||||
};
|
||||
let face = start_sketch_on_face(solid, tag, exec_state, args).await?;
|
||||
|
||||
// Create artifact used only by the UI, not the engine.
|
||||
let id = exec_state.next_uuid();
|
||||
exec_state.add_artifact(Artifact::StartSketchOnFace(StartSketchOnFace {
|
||||
id: ArtifactId::from(id),
|
||||
face_id: face.artifact_id,
|
||||
code_ref: CodeRef::placeholder(args.source_range),
|
||||
}));
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
{
|
||||
// Create artifact used only by the UI, not the engine.
|
||||
let id = exec_state.next_uuid();
|
||||
exec_state.add_artifact(Artifact::StartSketchOnFace(StartSketchOnFace {
|
||||
id: ArtifactId::from(id),
|
||||
face_id: face.artifact_id,
|
||||
code_ref: CodeRef::placeholder(args.source_range),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(SketchSurface::Face(face))
|
||||
}
|
||||
@ -1222,6 +1229,7 @@ async fn start_sketch_on_face(
|
||||
|
||||
Ok(Box::new(Face {
|
||||
id: extrude_plane_id,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_id: extrude_plane_id.into(),
|
||||
value: tag.to_string(),
|
||||
// TODO: get this from the extrude plane data.
|
||||
@ -1401,6 +1409,7 @@ pub(crate) async fn inner_start_profile(
|
||||
let sketch = Sketch {
|
||||
id: path_id,
|
||||
original_id: path_id,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_id: path_id.into(),
|
||||
on: sketch_surface.clone(),
|
||||
paths: vec![],
|
||||
|
@ -195,6 +195,7 @@ async fn inner_sweep(
|
||||
solids.push(
|
||||
do_post_extrude(
|
||||
sketch,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
id.into(),
|
||||
TyF64::new(0.0, NumericType::mm()),
|
||||
sectional.unwrap_or(false),
|
||||
|
@ -11,14 +11,13 @@ use kcmc::{
|
||||
};
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
|
||||
use super::args::TyF64;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{types::RuntimeType, ExecState, KclValue, SolidOrSketchOrImportedGeometry},
|
||||
std::Args,
|
||||
};
|
||||
|
||||
use super::args::TyF64;
|
||||
|
||||
/// Scale a solid or a sketch.
|
||||
pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let objects = args.get_unlabeled_kw_arg_typed(
|
||||
|
@ -2,9 +2,8 @@ use std::f64::consts::PI;
|
||||
|
||||
use kittycad_modeling_cmds::shared::Angle;
|
||||
|
||||
use crate::execution::types::{NumericType, UnitLen};
|
||||
|
||||
use super::args::TyF64;
|
||||
use crate::execution::types::{NumericType, UnitLen};
|
||||
|
||||
pub(crate) fn untype_point(p: [TyF64; 2]) -> ([f64; 2], NumericType) {
|
||||
let (x, y, ty) = NumericType::combine_eq(p[0].clone(), p[1].clone());
|
||||
@ -236,9 +235,10 @@ pub fn calculate_circle_from_3_points(points: [Coords2d; 3]) -> CircleParams {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// Here you can bring your functions into scope
|
||||
use std::f64::consts::TAU;
|
||||
|
||||
use approx::assert_relative_eq;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::f64::consts::TAU;
|
||||
|
||||
use super::{calculate_circle_center, get_x_component, get_y_component, Angle};
|
||||
|
||||
|
@ -21,7 +21,7 @@ pub struct RequestBody {
|
||||
pub async fn execute_and_snapshot(code: &str, current_file: Option<PathBuf>) -> Result<image::DynamicImage, ExecError> {
|
||||
let ctx = new_context(true, current_file).await?;
|
||||
let program = Program::parse_no_errs(code).map_err(KclErrorWithOutputs::no_outputs)?;
|
||||
let res = do_execute_and_snapshot(&ctx, program)
|
||||
let res = do_execute_and_snapshot(&ctx, program, false)
|
||||
.await
|
||||
.map(|(_, _, snap)| snap)
|
||||
.map_err(|err| err.error);
|
||||
@ -31,13 +31,14 @@ pub async fn execute_and_snapshot(code: &str, current_file: Option<PathBuf>) ->
|
||||
|
||||
/// Executes a kcl program and takes a snapshot of the result.
|
||||
/// This returns the bytes of the snapshot.
|
||||
pub async fn execute_and_snapshot_ast(
|
||||
#[cfg(test)]
|
||||
pub async fn execute_and_snapshot_ast_single_threaded(
|
||||
ast: Program,
|
||||
current_file: Option<PathBuf>,
|
||||
with_export_step: bool,
|
||||
) -> Result<(ExecState, EnvironmentRef, image::DynamicImage, Option<Vec<u8>>), ExecErrorWithState> {
|
||||
let ctx = new_context(true, current_file).await?;
|
||||
let (exec_state, env, img) = match do_execute_and_snapshot(&ctx, ast).await {
|
||||
let (exec_state, env, img) = match do_execute_and_snapshot(&ctx, ast, true).await {
|
||||
Ok((exec_state, env_ref, img)) => (exec_state, env_ref, img),
|
||||
Err(err) => {
|
||||
// If there was an error executing the program, return it.
|
||||
@ -72,7 +73,7 @@ pub async fn execute_and_snapshot_no_auth(
|
||||
) -> Result<(image::DynamicImage, EnvironmentRef), ExecError> {
|
||||
let ctx = new_context(false, current_file).await?;
|
||||
let program = Program::parse_no_errs(code).map_err(KclErrorWithOutputs::no_outputs)?;
|
||||
let res = do_execute_and_snapshot(&ctx, program)
|
||||
let res = do_execute_and_snapshot(&ctx, program, false)
|
||||
.await
|
||||
.map(|(_, env_ref, snap)| (snap, env_ref))
|
||||
.map_err(|err| err.error);
|
||||
@ -83,12 +84,15 @@ pub async fn execute_and_snapshot_no_auth(
|
||||
async fn do_execute_and_snapshot(
|
||||
ctx: &ExecutorContext,
|
||||
program: Program,
|
||||
single_threaded: bool,
|
||||
) -> Result<(ExecState, EnvironmentRef, image::DynamicImage), ExecErrorWithState> {
|
||||
let mut exec_state = ExecState::new(ctx);
|
||||
let result = ctx
|
||||
.run_single_threaded(&program, &mut exec_state)
|
||||
.await
|
||||
.map_err(|err| ExecErrorWithState::new(err.into(), exec_state.clone()))?;
|
||||
let result = if single_threaded {
|
||||
ctx.run_single_threaded(&program, &mut exec_state).await
|
||||
} else {
|
||||
ctx.run(&program, &mut exec_state).await
|
||||
}
|
||||
.map_err(|err| ExecErrorWithState::new(err.into(), exec_state.clone()))?;
|
||||
for e in exec_state.errors() {
|
||||
if e.severity.is_err() {
|
||||
return Err(ExecErrorWithState::new(
|
||||
|
@ -188,8 +188,10 @@ pub(crate) async fn import_universe(
|
||||
continue;
|
||||
}
|
||||
|
||||
let source_range = SourceRange::from(import_stmt.clone());
|
||||
let attrs = &import_stmt.outer_attrs;
|
||||
let module_id = ctx
|
||||
.open_module(&import_stmt.path, &[], exec_state, Default::default())
|
||||
.open_module(&import_stmt.path, attrs, exec_state, source_range)
|
||||
.await?;
|
||||
|
||||
let repr = {
|
||||
|
Reference in New Issue
Block a user