Compare commits
1 Commits
jtran/fix-
...
jtran/anim
Author | SHA1 | Date | |
---|---|---|---|
bfefa0f51a |
@ -15,14 +15,14 @@ import "car-tire.kcl" as carTire
|
|||||||
import * from "parameters.kcl"
|
import * from "parameters.kcl"
|
||||||
|
|
||||||
// Place the car rotor
|
// Place the car rotor
|
||||||
carRotor
|
rotor = carRotor
|
||||||
|> translate(x = 0, y = 0.5, z = 0)
|
|> translate(x = 0, y = 0.5, z = 0)
|
||||||
|
|
||||||
// Place the car wheel
|
// Place the car wheel
|
||||||
carWheel
|
carWheel
|
||||||
|
|
||||||
// Place the lug nuts
|
// Place the lug nuts
|
||||||
lugNut
|
lgnut = lugNut
|
||||||
|> patternCircular3d(
|
|> patternCircular3d(
|
||||||
arcDegrees = 360,
|
arcDegrees = 360,
|
||||||
axis = [0, 1, 0],
|
axis = [0, 1, 0],
|
||||||
@ -32,8 +32,19 @@ lugNut
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Place the brake caliper
|
// Place the brake caliper
|
||||||
brakeCaliper
|
cal = brakeCaliper
|
||||||
|> translate(x = 0, y = 0.5, z = 0)
|
|> translate(x = 0, y = 0.5, z = 0)
|
||||||
|
|
||||||
// Place the car tire
|
// Place the car tire
|
||||||
carTire
|
carTire
|
||||||
|
|
||||||
|
|
||||||
|
fn animate(step: number(_)) {
|
||||||
|
angle = 0.6deg
|
||||||
|
rotate(rotor, pitch = angle)
|
||||||
|
rotate(lgnut, pitch = angle)
|
||||||
|
rotate(cal, pitch = angle)
|
||||||
|
rotate(carWheel, pitch = angle)
|
||||||
|
rotate(carTire, pitch = angle)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
@ -369,7 +369,7 @@ profile007 = startProfile(
|
|||||||
|> line(%, endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(%, endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|> close(%)
|
|> close(%)
|
||||||
profile008 = circle(sketch005, center = [0, 0], diameter = nubDiameter)
|
profile008 = circle(sketch005, center = [0, 0], diameter = nubDiameter)
|
||||||
subtract2d(profile007, tool = profile008)
|
hourHand = subtract2d(profile007, tool = profile008)
|
||||||
|> extrude(%, length = 5)
|
|> extrude(%, length = 5)
|
||||||
|> appearance(%, color = "#404040")
|
|> appearance(%, color = "#404040")
|
||||||
|
|
||||||
@ -413,7 +413,7 @@ profile009 = startProfile(
|
|||||||
|> line(%, endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(%, endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|> close(%)
|
|> close(%)
|
||||||
profile010 = circle(sketch006, center = [0, 0], diameter = 30)
|
profile010 = circle(sketch006, center = [0, 0], diameter = 30)
|
||||||
subtract2d(profile009, tool = profile010)
|
minuteHand = subtract2d(profile009, tool = profile010)
|
||||||
|> extrude(%, length = 5)
|
|> extrude(%, length = 5)
|
||||||
|> appearance(%, color = "#404040")
|
|> appearance(%, color = "#404040")
|
||||||
|
|
||||||
@ -439,3 +439,8 @@ profile004 = startProfile(sketch003, at = [-slotWidth / 2, 200])
|
|||||||
|> extrude(%, length = -20)
|
|> extrude(%, length = -20)
|
||||||
|
|
||||||
// todo: create cavity for the screw to slide into (need csg update)
|
// todo: create cavity for the screw to slide into (need csg update)
|
||||||
|
|
||||||
|
fn animate(step: number(_)) {
|
||||||
|
rotate(hourHand, yaw = -0.1deg)
|
||||||
|
return rotate(minuteHand, yaw = -0.6deg)
|
||||||
|
}
|
||||||
|
@ -805,6 +805,43 @@ impl ExecutorContext {
|
|||||||
Ok(outcome)
|
Ok(outcome)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn run_additional(&self, program: crate::Program) -> Result<ExecOutcome, KclErrorWithOutputs> {
|
||||||
|
assert!(!self.is_mock());
|
||||||
|
|
||||||
|
let (program, exec_state, result) = match cache::read_old_ast().await {
|
||||||
|
Some(cached_state) => {
|
||||||
|
let mut exec_state = cached_state.reconstitute_exec_state();
|
||||||
|
exec_state.mut_stack().restore_env(cached_state.main.result_env);
|
||||||
|
|
||||||
|
let result = self.run_concurrent(&program, &mut exec_state, None, true).await;
|
||||||
|
|
||||||
|
(program, exec_state, result)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let mut exec_state = ExecState::new(self);
|
||||||
|
|
||||||
|
let result = self.run_concurrent(&program, &mut exec_state, None, false).await;
|
||||||
|
|
||||||
|
(program, exec_state, result)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Throw the error.
|
||||||
|
let result = result?;
|
||||||
|
|
||||||
|
// Save this as the last successful execution to the cache.
|
||||||
|
cache::write_old_ast(GlobalState::new(
|
||||||
|
exec_state.clone(),
|
||||||
|
self.settings.clone(),
|
||||||
|
program.ast,
|
||||||
|
result.0,
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let outcome = exec_state.into_exec_outcome(result.0, self).await;
|
||||||
|
Ok(outcome)
|
||||||
|
}
|
||||||
|
|
||||||
/// Perform the execution of a program.
|
/// Perform the execution of a program.
|
||||||
///
|
///
|
||||||
/// To access non-fatal errors and warnings, extract them from the `ExecState`.
|
/// To access non-fatal errors and warnings, extract them from the `ExecState`.
|
||||||
|
@ -111,6 +111,48 @@ impl Context {
|
|||||||
ctx.run_with_caching(program).await
|
ctx.run_with_caching(program).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Execute an additional program using the cache.
|
||||||
|
#[wasm_bindgen(js_name = executeAdditional)]
|
||||||
|
pub async fn execute_additional(
|
||||||
|
&self,
|
||||||
|
program_ast_json: &str,
|
||||||
|
path: Option<String>,
|
||||||
|
settings: &str,
|
||||||
|
) -> Result<JsValue, JsValue> {
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
|
self.execute_additional_typed(program_ast_json, path, settings)
|
||||||
|
.await
|
||||||
|
.and_then(|outcome| {
|
||||||
|
JsValue::from_serde(&outcome).map_err(|e| {
|
||||||
|
// The serde-wasm-bindgen does not work here because of weird HashMap issues.
|
||||||
|
// DO NOT USE serde_wasm_bindgen::to_value it will break the frontend.
|
||||||
|
KclErrorWithOutputs::no_outputs(KclError::internal(format!(
|
||||||
|
"Could not serialize successful KCL result. {TRUE_BUG} Details: {e}"
|
||||||
|
)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map_err(|e: KclErrorWithOutputs| JsValue::from_serde(&e).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn execute_additional_typed(
|
||||||
|
&self,
|
||||||
|
program_ast_json: &str,
|
||||||
|
path: Option<String>,
|
||||||
|
settings: &str,
|
||||||
|
) -> Result<ExecOutcome, KclErrorWithOutputs> {
|
||||||
|
let program: Program = serde_json::from_str(program_ast_json).map_err(|e| {
|
||||||
|
let err = KclError::internal(format!("Could not deserialize KCL AST. {TRUE_BUG} Details: {e}"));
|
||||||
|
KclErrorWithOutputs::no_outputs(err)
|
||||||
|
})?;
|
||||||
|
let ctx = self.create_executor_ctx(settings, path, false).map_err(|e| {
|
||||||
|
KclErrorWithOutputs::no_outputs(KclError::internal(format!(
|
||||||
|
"Could not create KCL executor context. {TRUE_BUG} Details: {e}"
|
||||||
|
)))
|
||||||
|
})?;
|
||||||
|
ctx.run_additional(program).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Reset the scene and bust the cache.
|
/// Reset the scene and bust the cache.
|
||||||
/// ONLY use this if you absolutely need to reset the scene and bust the cache.
|
/// ONLY use this if you absolutely need to reset the scene and bust the cache.
|
||||||
#[wasm_bindgen(js_name = bustCacheAndResetScene)]
|
#[wasm_bindgen(js_name = bustCacheAndResetScene)]
|
||||||
|
@ -7,6 +7,8 @@ import type { CustomIconName } from '@src/components/CustomIcon'
|
|||||||
import Tooltip from '@src/components/Tooltip'
|
import Tooltip from '@src/components/Tooltip'
|
||||||
|
|
||||||
import styles from './ModelingPane.module.css'
|
import styles from './ModelingPane.module.css'
|
||||||
|
import { reportRejection } from '@src/lib/trap'
|
||||||
|
import { kclManager } from '@src/lib/singletons'
|
||||||
|
|
||||||
export interface ModelingPaneProps {
|
export interface ModelingPaneProps {
|
||||||
id: string
|
id: string
|
||||||
@ -19,6 +21,28 @@ export interface ModelingPaneProps {
|
|||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ANIMATE_INTERVAL = 16 // milliseconds
|
||||||
|
let animateTimeout: NodeJS.Timeout | null = null
|
||||||
|
|
||||||
|
function doAnimate() {
|
||||||
|
;(async () => {
|
||||||
|
await kclManager.executeAnimate()
|
||||||
|
if (animateTimeout !== null) {
|
||||||
|
animateTimeout = setTimeout(doAnimate, ANIMATE_INTERVAL)
|
||||||
|
}
|
||||||
|
})().catch(reportRejection)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPlay() {
|
||||||
|
console.log('Play button clicked')
|
||||||
|
if (animateTimeout) {
|
||||||
|
clearTimeout(animateTimeout)
|
||||||
|
animateTimeout = null
|
||||||
|
} else {
|
||||||
|
animateTimeout = setTimeout(doAnimate, ANIMATE_INTERVAL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const ModelingPaneHeader = ({
|
export const ModelingPaneHeader = ({
|
||||||
id,
|
id,
|
||||||
icon,
|
icon,
|
||||||
@ -40,6 +64,20 @@ export const ModelingPaneHeader = ({
|
|||||||
)}
|
)}
|
||||||
<span data-testid={id + '-header'}>{title}</span>
|
<span data-testid={id + '-header'}>{title}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{id === 'code' && (
|
||||||
|
<ActionButton
|
||||||
|
Element="button"
|
||||||
|
iconStart={{
|
||||||
|
icon: 'play',
|
||||||
|
iconClassName: '!text-current',
|
||||||
|
bgClassName: 'bg-transparent dark:bg-transparent',
|
||||||
|
}}
|
||||||
|
className="!p-0 !bg-transparent hover:text-primary border-transparent dark:!border-transparent hover:!border-primary dark:hover:!border-chalkboard-70 !outline-none"
|
||||||
|
onClick={() => onPlay()}
|
||||||
|
>
|
||||||
|
<Tooltip position="bottom-right">Play</Tooltip>
|
||||||
|
</ActionButton>
|
||||||
|
)}
|
||||||
{Menu instanceof Function ? <Menu /> : Menu}
|
{Menu instanceof Function ? <Menu /> : Menu}
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
|
@ -17,7 +17,12 @@ import {
|
|||||||
compilationErrorsToDiagnostics,
|
compilationErrorsToDiagnostics,
|
||||||
kclErrorsToDiagnostics,
|
kclErrorsToDiagnostics,
|
||||||
} from '@src/lang/errors'
|
} from '@src/lang/errors'
|
||||||
import { executeAst, executeAstMock, lintAst } from '@src/lang/langHelpers'
|
import {
|
||||||
|
executeAdditional,
|
||||||
|
executeAst,
|
||||||
|
executeAstMock,
|
||||||
|
lintAst,
|
||||||
|
} from '@src/lang/langHelpers'
|
||||||
import { getNodeFromPath, getSettingsAnnotation } from '@src/lang/queryAst'
|
import { getNodeFromPath, getSettingsAnnotation } from '@src/lang/queryAst'
|
||||||
import { CommandLogType } from '@src/lang/std/commandLog'
|
import { CommandLogType } from '@src/lang/std/commandLog'
|
||||||
import type { EngineCommandManager } from '@src/lang/std/engineConnection'
|
import type { EngineCommandManager } from '@src/lang/std/engineConnection'
|
||||||
@ -106,6 +111,7 @@ export class KclManager extends EventTarget {
|
|||||||
preComments: [],
|
preComments: [],
|
||||||
commentStart: 0,
|
commentStart: 0,
|
||||||
}
|
}
|
||||||
|
private _animateState = { step: 0 }
|
||||||
private _execState: ExecState = emptyExecState()
|
private _execState: ExecState = emptyExecState()
|
||||||
private _variables: VariableMap = {}
|
private _variables: VariableMap = {}
|
||||||
lastSuccessfulVariables: VariableMap = {}
|
lastSuccessfulVariables: VariableMap = {}
|
||||||
@ -450,6 +456,7 @@ export class KclManager extends EventTarget {
|
|||||||
const ast = args.ast || this.ast
|
const ast = args.ast || this.ast
|
||||||
markOnce('code/startExecuteAst')
|
markOnce('code/startExecuteAst')
|
||||||
|
|
||||||
|
this._animateState.step = 0
|
||||||
const currentExecutionId = args.executionId || Date.now()
|
const currentExecutionId = args.executionId || Date.now()
|
||||||
this._cancelTokens.set(currentExecutionId, false)
|
this._cancelTokens.set(currentExecutionId, false)
|
||||||
|
|
||||||
@ -541,6 +548,39 @@ export class KclManager extends EventTarget {
|
|||||||
markOnce('code/endExecuteAst')
|
markOnce('code/endExecuteAst')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async executeAnimate(): Promise<void> {
|
||||||
|
if (this.isExecuting) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const code = `animate(step = ${this._animateState.step})`
|
||||||
|
const result = parse(code)
|
||||||
|
if (err(result)) {
|
||||||
|
console.error(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const program = result.program
|
||||||
|
if (!program) {
|
||||||
|
console.error('No program returned from parse')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { errors } = await executeAdditional({
|
||||||
|
ast: program,
|
||||||
|
path: this.singletons.codeManager.currentFilePath || undefined,
|
||||||
|
rustContext: this.singletons.rustContext,
|
||||||
|
})
|
||||||
|
if (errors.length > 0) {
|
||||||
|
console.error('Errors executing animate:', errors)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this._animateState.step += 1
|
||||||
|
if (this._animateState.step === Number.MAX_SAFE_INTEGER) {
|
||||||
|
this._animateState.step = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DO NOT CALL THIS from codemirror ever.
|
// DO NOT CALL THIS from codemirror ever.
|
||||||
async executeAstMock(ast: Program): Promise<null | Error> {
|
async executeAstMock(ast: Program): Promise<null | Error> {
|
||||||
await this.ensureWasmInit()
|
await this.ensureWasmInit()
|
||||||
|
@ -84,6 +84,31 @@ export async function executeAst({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function executeAdditional({
|
||||||
|
ast,
|
||||||
|
rustContext,
|
||||||
|
path,
|
||||||
|
}: {
|
||||||
|
ast: Node<Program>
|
||||||
|
rustContext: RustContext
|
||||||
|
path?: string
|
||||||
|
}): Promise<ExecutionResult> {
|
||||||
|
try {
|
||||||
|
const settings = await jsAppSettings()
|
||||||
|
const execState = await rustContext.executeAdditional(ast, settings, path)
|
||||||
|
|
||||||
|
await rustContext.waitForAllEngineCommands()
|
||||||
|
return {
|
||||||
|
logs: [],
|
||||||
|
errors: [],
|
||||||
|
execState,
|
||||||
|
isInterrupted: false,
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
return handleExecuteError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function executeAstMock({
|
export async function executeAstMock({
|
||||||
ast,
|
ast,
|
||||||
rustContext,
|
rustContext,
|
||||||
|
@ -96,6 +96,34 @@ export default class RustContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Execute an additional program using the cache. */
|
||||||
|
async executeAdditional(
|
||||||
|
node: Node<Program>,
|
||||||
|
settings: DeepPartial<Configuration>,
|
||||||
|
path?: string
|
||||||
|
): Promise<ExecState> {
|
||||||
|
const instance = await this._checkInstance()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await instance.executeAdditional(
|
||||||
|
JSON.stringify(node),
|
||||||
|
path,
|
||||||
|
JSON.stringify(settings)
|
||||||
|
)
|
||||||
|
// Set the default planes, safe to call after execute.
|
||||||
|
const outcome = execStateFromRust(result)
|
||||||
|
|
||||||
|
this._defaultPlanes = outcome.defaultPlanes
|
||||||
|
|
||||||
|
// Return the result.
|
||||||
|
return outcome
|
||||||
|
} catch (e: any) {
|
||||||
|
const err = errFromErrWithOutputs(e)
|
||||||
|
this._defaultPlanes = err.defaultPlanes
|
||||||
|
return Promise.reject(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Execute a program with in mock mode. */
|
/** Execute a program with in mock mode. */
|
||||||
async executeMock(
|
async executeMock(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
|
Reference in New Issue
Block a user