Compare commits

..

1 Commits

Author SHA1 Message Date
e1f1051fe0 Grackle: Add Paul's QR code as a unit test 2024-03-17 10:54:42 -05:00
38 changed files with 3835 additions and 1880 deletions

View File

@ -1,3 +1,3 @@
[codespell]
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo
skip: **/target,node_modules,build,**/Cargo.lock

50
.github/workflows/cargo-build.yml vendored Normal file
View File

@ -0,0 +1,50 @@
on:
push:
branches:
- main
paths:
- '**.rs'
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- .github/workflows/cargo-build.yml
pull_request:
paths:
- '**.rs'
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- .github/workflows/cargo-build.yml
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
name: cargo build
jobs:
cargobuild:
name: cargo build
runs-on: ubuntu-latest
strategy:
matrix:
dir: ['src/wasm-lib']
steps:
- uses: actions/checkout@v4
- name: Install latest rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: install dependencies
if: matrix.dir == 'src-tauri'
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
- name: Rust Cache
uses: Swatinem/rust-cache@v2.6.1
- name: Run cargo build
run: |
cd "${{ matrix.dir }}"
cargo build --all
shell: bash

View File

@ -9,6 +9,12 @@ on:
- '**.rs'
- .github/workflows/cargo-clippy.yml
pull_request:
paths:
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- '**.rs'
- .github/workflows/cargo-build.yml
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

View File

@ -1,36 +0,0 @@
name: Check Onboarding KCL
on:
pull_request:
types: [opened, synchronize]
paths:
- 'src/lib/exampleKcl.ts'
permissions:
contents: read
issues: write
pull-requests: write
jobs:
comment:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Comment on PR
uses: actions/github-script@v6
with:
script: |
const message = '`src/lib/exampleKcl.ts` has been updated in this PR, please review and update the `src/routes/onboarding`, if needed.';
const issue_number = context.payload.pull_request.number;
const owner = context.repo.owner;
const repo = context.repo.repo;
// Post a comment on the PR
await github.rest.issues.createComment({
owner,
repo,
issue_number,
body: message,
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -1,6 +1,6 @@
{
"name": "untitled-app",
"version": "0.16.0",
"version": "0.15.6",
"private": true,
"dependencies": {
"@codemirror/autocomplete": "^6.10.2",

2
src-tauri/Cargo.lock generated
View File

@ -3876,7 +3876,7 @@ dependencies = [
[[package]]
name = "tauri-plugin-fs-extra"
version = "0.0.0"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#6db4320e98a4278d605dd05d4d87e1433939a7cd"
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#d95a1b382f512a0be748e7a89e0f8fc4278010e2"
dependencies = [
"log",
"serde",

View File

@ -7,7 +7,7 @@
},
"package": {
"productName": "zoo-modeling-app",
"version": "0.16.0"
"version": "0.15.6"
},
"tauri": {
"allowlist": {

View File

@ -59,7 +59,6 @@ const router = createBrowserRouter([
{
loader: fileLoader,
id: paths.FILE,
path: paths.FILE + '/:id',
element: (
<Auth>
<FileMachineProvider>
@ -75,11 +74,8 @@ const router = createBrowserRouter([
),
children: [
{
path: paths.FILE + '/:id',
loader: onboardingRedirectLoader,
index: true,
element: <></>,
},
{
children: [
{
path: makeUrlPathRelative(paths.SETTINGS),

View File

@ -30,7 +30,7 @@ describe('NetworkHealthIndicator tests', () => {
fireEvent.click(screen.getByTestId('network-toggle'))
expect(screen.getByTestId('network')).toHaveTextContent(
NETWORK_HEALTH_TEXT[NetworkHealthState.Ok]
NETWORK_HEALTH_TEXT[NetworkHealthState.Issue]
)
})

View File

@ -6,7 +6,8 @@ import {
ConnectingTypeGroup,
DisconnectingType,
engineCommandManager,
EngineConnectionState,
EngineCommandManagerEvents,
EngineConnectionEvents,
EngineConnectionStateType,
ErrorType,
initialConnectingTypeGroupState,
@ -81,37 +82,35 @@ const overallConnectionStateIcon: Record<
}
export function useNetworkStatus() {
const [steps, setSteps] = useState(initialConnectingTypeGroupState)
const [steps, setSteps] = useState(
structuredClone(initialConnectingTypeGroupState)
)
const [internetConnected, setInternetConnected] = useState<boolean>(true)
const [overallState, setOverallState] = useState<NetworkHealthState>(
NetworkHealthState.Ok
NetworkHealthState.Disconnected
)
const [pingPongHealth, setPingPongHealth] = useState<'OK' | 'BAD'>('BAD')
const [hasCopied, setHasCopied] = useState<boolean>(false)
const [error, setError] = useState<ErrorType | undefined>(undefined)
const issues: Record<ConnectingTypeGroup, boolean> = {
[ConnectingTypeGroup.WebSocket]: steps[ConnectingTypeGroup.WebSocket].some(
(a: [ConnectingType, boolean | undefined]) => a[1] === false
),
[ConnectingTypeGroup.ICE]: steps[ConnectingTypeGroup.ICE].some(
(a: [ConnectingType, boolean | undefined]) => a[1] === false
),
[ConnectingTypeGroup.WebRTC]: steps[ConnectingTypeGroup.WebRTC].some(
(a: [ConnectingType, boolean | undefined]) => a[1] === false
),
}
const hasIssue = (i: [ConnectingType, boolean | undefined]) =>
i[1] === undefined ? i[1] : !i[1]
const hasIssues: boolean =
issues[ConnectingTypeGroup.WebSocket] ||
issues[ConnectingTypeGroup.ICE] ||
issues[ConnectingTypeGroup.WebRTC]
const [issues, setIssues] = useState<
Record<ConnectingTypeGroup, boolean | undefined>
>({
[ConnectingTypeGroup.WebSocket]: undefined,
[ConnectingTypeGroup.ICE]: undefined,
[ConnectingTypeGroup.WebRTC]: undefined,
})
const [hasIssues, setHasIssues] = useState<boolean | undefined>(undefined)
useEffect(() => {
setOverallState(
!internetConnected
? NetworkHealthState.Disconnected
: hasIssues
: hasIssues || hasIssues === undefined
? NetworkHealthState.Issue
: NetworkHealthState.Ok
)
@ -134,19 +133,59 @@ export function useNetworkStatus() {
}, [])
useEffect(() => {
engineCommandManager.onConnectionStateChange(
(engineConnectionState: EngineConnectionState) => {
let hasSetAStep = false
console.log(pingPongHealth)
}, [pingPongHealth])
useEffect(() => {
const issues = {
[ConnectingTypeGroup.WebSocket]: steps[
ConnectingTypeGroup.WebSocket
].reduce(
(acc: boolean | undefined, a) =>
acc === true || acc === undefined ? acc : hasIssue(a),
false
),
[ConnectingTypeGroup.ICE]: steps[ConnectingTypeGroup.ICE].reduce(
(acc: boolean | undefined, a) =>
acc === true || acc === undefined ? acc : hasIssue(a),
false
),
[ConnectingTypeGroup.WebRTC]: steps[ConnectingTypeGroup.WebRTC].reduce(
(acc: boolean | undefined, a) =>
acc === true || acc === undefined ? acc : hasIssue(a),
false
),
}
setIssues(issues)
}, [steps])
useEffect(() => {
setHasIssues(
issues[ConnectingTypeGroup.WebSocket] ||
issues[ConnectingTypeGroup.ICE] ||
issues[ConnectingTypeGroup.WebRTC]
)
}, [issues])
useEffect(() => {
const onPingPongChange = ({ detail: state }: CustomEvent) => {
setPingPongHealth(state)
}
const onConnectionStateChange = ({
detail: engineConnectionState,
}: CustomEvent) => {
setSteps((steps) => {
let nextSteps = structuredClone(steps)
if (
engineConnectionState.type === EngineConnectionStateType.Connecting
) {
const groups = Object.values(steps)
const groups = Object.values(nextSteps)
for (let group of groups) {
for (let step of group) {
if (step[0] !== engineConnectionState.value.type) continue
step[1] = true
hasSetAStep = true
}
}
}
@ -154,7 +193,7 @@ export function useNetworkStatus() {
if (
engineConnectionState.type === EngineConnectionStateType.Disconnecting
) {
const groups = Object.values(steps)
const groups = Object.values(nextSteps)
for (let group of groups) {
for (let step of group) {
if (
@ -165,7 +204,6 @@ export function useNetworkStatus() {
?.type === step[0]
) {
step[1] = false
hasSetAStep = true
}
}
}
@ -176,11 +214,50 @@ export function useNetworkStatus() {
}
}
if (hasSetAStep) {
setSteps(steps)
// Reset the state of all steps if we have disconnected.
if (
engineConnectionState.type === EngineConnectionStateType.Disconnected
) {
return structuredClone(initialConnectingTypeGroupState)
}
}
return nextSteps
})
}
const onEngineAvailable = ({ detail: engineConnection }: CustomEvent) => {
engineConnection.addEventListener(
EngineConnectionEvents.PingPongChanged,
onPingPongChange as EventListener
)
engineConnection.addEventListener(
EngineConnectionEvents.ConnectionStateChanged,
onConnectionStateChange as EventListener
)
}
engineCommandManager.addEventListener(
EngineCommandManagerEvents.EngineAvailable,
onEngineAvailable as EventListener
)
return () => {
engineCommandManager.removeEventListener(
EngineCommandManagerEvents.EngineAvailable,
onEngineAvailable as EventListener
)
// When the component is unmounted these should be assigned, but it's possible
// the component mounts and unmounts before engine is available.
engineCommandManager.engineConnection?.addEventListener(
EngineConnectionEvents.PingPongChanged,
onPingPongChange as EventListener
)
engineCommandManager.engineConnection?.addEventListener(
EngineConnectionEvents.ConnectionStateChanged,
onConnectionStateChange as EventListener
)
}
}, [])
return {
@ -192,6 +269,7 @@ export function useNetworkStatus() {
error,
setHasCopied,
hasCopied,
pingPongHealth,
}
}
@ -256,18 +334,18 @@ export const NetworkHealthIndicator = () => {
size="lg"
icon={
hasIssueToIcon[
issues[name as ConnectingTypeGroup].toString()
String(issues[name as ConnectingTypeGroup])
]
}
iconClassName={
hasIssueToIconColors[
issues[name as ConnectingTypeGroup].toString()
String(issues[name as ConnectingTypeGroup])
].icon
}
bgClassName={
'rounded-sm ' +
hasIssueToIconColors[
issues[name as ConnectingTypeGroup].toString()
String(issues[name as ConnectingTypeGroup])
].bg
}
/>

View File

@ -32,6 +32,7 @@ export const Stream = ({ className = '' }: { className?: string }) => {
const { state } = useModelingContext()
const { isExecuting } = useKclContext()
const { overallState } = useNetworkStatus()
const isNetworkOkay = overallState === NetworkHealthState.Ok
useEffect(() => {

View File

@ -1,5 +1,5 @@
import { PathToNode, Program, SourceRange } from 'lang/wasm'
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_CONNECTION_TIMEOUT_MS } from 'env'
import { VITE_KC_API_WS_MODELING_URL } from 'env'
import { Models } from '@kittycad/lib'
import { exportSave } from 'lib/exportSave'
import { v4 as uuidv4 } from 'uuid'
@ -8,6 +8,9 @@ import { sceneInfra } from 'clientSideScene/sceneInfra'
let lastMessage = ''
// TODO(paultag): This ought to be tweakable.
const pingIntervalMs = 10000
interface CommandInfo {
commandType: CommandTypes
range: SourceRange
@ -37,11 +40,6 @@ export interface ArtifactMap {
[key: string]: ResultCommand | PendingCommand | FailedCommand
}
interface NewTrackArgs {
conn: EngineConnection
mediaStream: MediaStream
}
// This looks funny, I know. This is needed because node and the browser
// disagree as to the type. In a browser it's a number, but in node it's a
// "Timeout".
@ -158,10 +156,28 @@ export type EngineConnectionState =
| State<EngineConnectionStateType.Disconnecting, DisconnectingValue>
| State<EngineConnectionStateType.Disconnected, void>
export type PingPongState = 'OK' | 'BAD'
export enum EngineConnectionEvents {
// Fires for each ping-pong success or failure.
PingPongChanged = 'ping-pong-changed', // (state: PingPongState) => void
// For now, this is only used by the NetworkHealthIndicator.
// We can eventually use it for more, but one step at a time.
ConnectionStateChanged = 'connection-state-changed', // (state: EngineConnectionState) => void
// These are used for the EngineCommandManager and were created
// before onConnectionStateChange existed.
ConnectionStarted = 'connection-started', // (engineConnection: EngineConnection) => void
Opened = 'opened', // (engineConnection: EngineConnection) => void
Closed = 'closed', // (engineConnection: EngineConnection) => void
NewTrack = 'new-track', // (track: NewTrackArgs) => void
}
// EngineConnection encapsulates the connection(s) to the Engine
// for the EngineCommandManager; namely, the underlying WebSocket
// and WebRTC connections.
class EngineConnection {
class EngineConnection extends EventTarget {
websocket?: WebSocket
pc?: RTCPeerConnection
unreliableDataChannel?: RTCDataChannel
@ -195,7 +211,12 @@ class EngineConnection {
}
}
this._state = next
this.onConnectionStateChange(this._state)
this.dispatchEvent(
new CustomEvent(EngineConnectionEvents.ConnectionStateChanged, {
detail: this._state,
})
)
}
private failedConnTimeout: Timeout | null
@ -203,74 +224,39 @@ class EngineConnection {
readonly url: string
private readonly token?: string
// For now, this is only used by the NetworkHealthIndicator.
// We can eventually use it for more, but one step at a time.
private onConnectionStateChange: (state: EngineConnectionState) => void
// These are used for the EngineCommandManager and were created
// before onConnectionStateChange existed.
private onEngineConnectionOpen: (engineConnection: EngineConnection) => void
private onConnectionStarted: (engineConnection: EngineConnection) => void
private onClose: (engineConnection: EngineConnection) => void
private onNewTrack: (track: NewTrackArgs) => void
// TODO: actual type is ClientMetrics
private webrtcStatsCollector?: () => Promise<ClientMetrics>
constructor({
url,
token,
onConnectionStateChange = () => {},
onNewTrack = () => {},
onEngineConnectionOpen = () => {},
onConnectionStarted = () => {},
onClose = () => {},
}: {
url: string
token?: string
onConnectionStateChange?: (state: EngineConnectionState) => void
onEngineConnectionOpen?: (engineConnection: EngineConnection) => void
onConnectionStarted?: (engineConnection: EngineConnection) => void
onClose?: (engineConnection: EngineConnection) => void
onNewTrack?: (track: NewTrackArgs) => void
}) {
private pingPongSpan: { ping?: Date; pong?: Date }
constructor({ url, token }: { url: string; token?: string }) {
super()
this.url = url
this.token = token
this.failedConnTimeout = null
this.onConnectionStateChange = onConnectionStateChange
this.onEngineConnectionOpen = onEngineConnectionOpen
this.onConnectionStarted = onConnectionStarted
this.onClose = onClose
this.onNewTrack = onNewTrack
// TODO(paultag): This ought to be tweakable.
const pingIntervalMs = 10000
this.pingPongSpan = { ping: undefined, pong: undefined }
// Without an interval ping, our connection will timeout.
let pingInterval = setInterval(() => {
setInterval(() => {
switch (this.state.type as EngineConnectionStateType) {
case EngineConnectionStateType.ConnectionEstablished:
this.send({ type: 'ping' })
this.pingPongSpan.ping = new Date()
break
case EngineConnectionStateType.Disconnecting:
case EngineConnectionStateType.Disconnected:
clearInterval(pingInterval)
// Reconnect if we have disconnected.
if (!this.isConnecting()) this.connect()
break
default:
if (this.isConnecting()) break
// Means we never could do an initial connection. Reconnect everything.
if (!this.pingPongSpan.ping) this.connect()
break
}
}, pingIntervalMs)
const connectionTimeoutMs = VITE_KC_CONNECTION_TIMEOUT_MS
let connectRetryInterval = setInterval(() => {
if (this.state.type !== EngineConnectionStateType.Disconnected) return
// Only try reconnecting when completely disconnected.
clearInterval(connectRetryInterval)
console.log('Trying to reconnect')
this.connect()
}, connectionTimeoutMs)
}
isConnecting() {
@ -352,7 +338,11 @@ class EngineConnection {
// dance is it safest to connect the video tracks / stream
case 'connected':
// Let the browser attach to the video stream now
this.onNewTrack({ conn: this, mediaStream: this.mediaStream! })
this.dispatchEvent(
new CustomEvent(EngineConnectionEvents.NewTrack, {
detail: { conn: this, mediaStream: this.mediaStream! },
})
)
break
case 'failed':
this.disconnectAll()
@ -468,7 +458,9 @@ class EngineConnection {
// Everything is now connected.
this.state = { type: EngineConnectionStateType.ConnectionEstablished }
this.onEngineConnectionOpen(this)
this.dispatchEvent(
new CustomEvent(EngineConnectionEvents.Opened, { detail: this })
)
})
this.unreliableDataChannel.addEventListener('close', (event) => {
@ -510,6 +502,10 @@ class EngineConnection {
},
}
// Send an initial ping
this.send({ type: 'ping' })
this.pingPongSpan.ping = new Date()
// This is required for when KCMA is running stand-alone / within Tauri.
// Otherwise when run in a browser, the token is sent implicitly via
// the Cookie header.
@ -575,12 +571,34 @@ failed cmd type was ${artifactThatFailed?.commandType}`
let resp = message.resp
// If there's no body to the response, we can bail here.
// !resp.type is usually "pong" response for our "ping"
if (!resp || !resp.type) {
return
}
switch (resp.type) {
case 'pong':
this.pingPongSpan.pong = new Date()
if (this.pingPongSpan.ping && this.pingPongSpan.pong) {
if (
Math.abs(
this.pingPongSpan.pong.valueOf() -
this.pingPongSpan.ping.valueOf()
) >= pingIntervalMs
) {
this.dispatchEvent(
new CustomEvent(EngineConnectionEvents.PingPongChanged, {
detail: 'BAD',
})
)
} else {
this.dispatchEvent(
new CustomEvent(EngineConnectionEvents.PingPongChanged, {
detail: 'OK',
})
)
}
}
break
case 'ice_server_info':
let ice_servers = resp.data?.ice_servers
@ -727,27 +745,11 @@ failed cmd type was ${artifactThatFailed?.commandType}`
}
})
const connectionTimeoutMs = VITE_KC_CONNECTION_TIMEOUT_MS
if (this.failedConnTimeout) {
clearTimeout(this.failedConnTimeout)
this.failedConnTimeout = null
}
this.failedConnTimeout = setTimeout(() => {
if (this.isReady()) {
return
}
this.failedConnTimeout = null
this.state = {
type: EngineConnectionStateType.Disconnecting,
value: {
type: DisconnectingType.Timeout,
},
}
this.disconnectAll()
this.finalizeIfAllConnectionsClosed()
}, connectionTimeoutMs)
this.onConnectionStarted(this)
this.dispatchEvent(
new CustomEvent(EngineConnectionEvents.ConnectionStarted, {
detail: this,
})
)
}
unreliableSend(message: object | string) {
// TODO(paultag): Add in logic to determine the connection state and
@ -796,6 +798,8 @@ interface UnreliableSubscription<T extends UnreliableResponses['type']> {
callback: (data: Extract<UnreliableResponses, { type: T }>) => void
}
// TODO: Should eventually be replaced with native EventTarget event system,
// as it manages events in a more familiar way to other developers.
export interface Subscription<T extends ModelTypes> {
event: T
callback: (
@ -823,7 +827,11 @@ export type CommandLog =
data: null
}
export class EngineCommandManager {
export enum EngineCommandManagerEvents {
EngineAvailable = 'engine-available',
}
export class EngineCommandManager extends EventTarget {
artifactMap: ArtifactMap = {}
lastArtifactMap: ArtifactMap = {}
sceneCommandArtifacts: ArtifactMap = {}
@ -857,10 +865,9 @@ export class EngineCommandManager {
}
} = {} as any
callbacksEngineStateConnection: ((state: EngineConnectionState) => void)[] =
[]
constructor() {
super()
this.engineConnection = undefined
;(async () => {
// circular dependency needs one to be lazy loaded
@ -901,12 +908,17 @@ export class EngineCommandManager {
this.engineConnection = new EngineConnection({
url,
token,
onConnectionStateChange: (state: EngineConnectionState) => {
for (let cb of this.callbacksEngineStateConnection) {
cb(state)
}
},
onEngineConnectionOpen: () => {
})
this.dispatchEvent(
new CustomEvent(EngineCommandManagerEvents.EngineAvailable, {
detail: this.engineConnection,
})
)
this.engineConnection.addEventListener(
EngineConnectionEvents.Opened,
() => {
// Make the axis gizmo.
// We do this after the connection opened to avoid a race condition.
// Connected opened is the last thing that happens when the stream
@ -941,78 +953,98 @@ export class EngineCommandManager {
setIsStreamReady(true)
executeCode(undefined, true)
})
},
onClose: () => {
setIsStreamReady(false)
},
onConnectionStarted: (engineConnection) => {
engineConnection?.pc?.addEventListener('datachannel', (event) => {
let unreliableDataChannel = event.channel
}
)
unreliableDataChannel.addEventListener('message', (event) => {
const result: UnreliableResponses = JSON.parse(event.data)
Object.values(
this.unreliableSubscriptions[result.type] || {}
).forEach(
// TODO: There is only one response that uses the unreliable channel atm,
// highlight_set_entity, if there are more it's likely they will all have the same
// sequence logic, but I'm not sure if we use a single global sequence or a sequence
// per unreliable subscription.
(callback) => {
if (
result?.data?.sequence &&
result?.data.sequence > this.inSequence &&
result.type === 'highlight_set_entity'
) {
this.inSequence = result.data.sequence
callback(result)
}
this.engineConnection.addEventListener(
EngineConnectionEvents.Closed,
() => {
setIsStreamReady(false)
}
)
this.engineConnection.addEventListener(
EngineConnectionEvents.ConnectionStarted,
(({ detail: engineConnection }: CustomEvent) => {
engineConnection?.pc?.addEventListener(
'datachannel',
(event: RTCDataChannelEvent) => {
let unreliableDataChannel = event.channel
unreliableDataChannel.addEventListener(
'message',
(event: MessageEvent) => {
const result: UnreliableResponses = JSON.parse(event.data)
Object.values(
this.unreliableSubscriptions[result.type] || {}
).forEach(
// TODO: There is only one response that uses the unreliable channel atm,
// highlight_set_entity, if there are more it's likely they will all have the same
// sequence logic, but I'm not sure if we use a single global sequence or a sequence
// per unreliable subscription.
(callback) => {
if (
result?.data?.sequence &&
result?.data.sequence > this.inSequence &&
result.type === 'highlight_set_entity'
) {
this.inSequence = result.data.sequence
callback(result)
}
}
)
}
)
})
})
}
)
// When the EngineConnection starts a connection, we want to register
// callbacks into the WebSocket/PeerConnection.
engineConnection.websocket?.addEventListener('message', (event) => {
if (event.data instanceof ArrayBuffer) {
// If the data is an ArrayBuffer, it's the result of an export command,
// because in all other cases we send JSON strings. But in the case of
// export we send a binary blob.
// Pass this to our export function.
void exportSave(event.data)
} else {
const message: Models['WebSocketResponse_type'] = JSON.parse(
event.data
)
if (
message.success &&
message.resp.type === 'modeling' &&
message.request_id
) {
this.handleModelingCommand(message.resp, message.request_id)
} else if (
!message.success &&
message.request_id &&
this.artifactMap[message.request_id]
) {
this.handleFailedModelingCommand(message)
engineConnection.websocket?.addEventListener(
'message',
(event: MessageEvent) => {
if (event.data instanceof ArrayBuffer) {
// If the data is an ArrayBuffer, it's the result of an export command,
// because in all other cases we send JSON strings. But in the case of
// export we send a binary blob.
// Pass this to our export function.
void exportSave(event.data)
} else {
const message: Models['WebSocketResponse_type'] = JSON.parse(
event.data
)
if (
message.success &&
message.resp.type === 'modeling' &&
message.request_id
) {
this.handleModelingCommand(message.resp, message.request_id)
} else if (
!message.success &&
message.request_id &&
this.artifactMap[message.request_id]
) {
this.handleFailedModelingCommand(message)
}
}
}
})
},
onNewTrack: ({ mediaStream }) => {
console.log('received track', mediaStream)
)
}) as EventListener
)
mediaStream.getVideoTracks()[0].addEventListener('mute', () => {
console.log('peer is not sending video to us')
// this.engineConnection?.close()
// this.engineConnection?.connect()
})
this.engineConnection.addEventListener(EngineConnectionEvents.NewTrack, (({
detail: { mediaStream },
}: CustomEvent) => {
console.log('received track', mediaStream)
setMediaStream(mediaStream)
},
})
mediaStream.getVideoTracks()[0].addEventListener('mute', () => {
console.log('peer is not sending video to us')
// this.engineConnection?.close()
// this.engineConnection?.connect()
})
setMediaStream(mediaStream)
}) as EventListener)
this.engineConnection?.connect()
}
@ -1202,9 +1234,6 @@ export class EngineCommandManager {
) {
delete this.unreliableSubscriptions[event][id]
}
onConnectionStateChange(callback: (state: EngineConnectionState) => void) {
this.callbacksEngineStateConnection.push(callback)
}
endSession() {
// TODO: instead of sending a single command with `object_ids: Object.keys(this.artifactMap)`
// we need to loop over them each individually because if the engine doesn't recognise a single

View File

@ -149,7 +149,7 @@ export const lineTo: SketchLineHelper = {
pathToNode,
}
},
addTag: addTag(),
addTag: addTagWithTo('default'),
}
export const line: SketchLineHelper = {
@ -240,7 +240,7 @@ export const line: SketchLineHelper = {
pathToNode,
}
},
addTag: addTag(),
addTag: addTagWithTo('default'),
}
export const xLineTo: SketchLineHelper = {
@ -288,7 +288,7 @@ export const xLineTo: SketchLineHelper = {
pathToNode,
}
},
addTag: addTag(),
addTag: addTagWithTo('default'),
}
export const yLineTo: SketchLineHelper = {
@ -336,7 +336,7 @@ export const yLineTo: SketchLineHelper = {
pathToNode,
}
},
addTag: addTag(),
addTag: addTagWithTo('default'),
}
export const xLine: SketchLineHelper = {
@ -386,7 +386,7 @@ export const xLine: SketchLineHelper = {
pathToNode,
}
},
addTag: addTag(),
addTag: addTagWithTo('length'),
}
export const yLine: SketchLineHelper = {
@ -430,7 +430,7 @@ export const yLine: SketchLineHelper = {
pathToNode,
}
},
addTag: addTag(),
addTag: addTagWithTo('length'),
}
export const tangentialArcTo: SketchLineHelper = {
@ -510,7 +510,7 @@ export const tangentialArcTo: SketchLineHelper = {
}
},
// TODO copy-paste from angledLine
addTag: addTag(),
addTag: addTagWithTo('angleLength'),
}
export const angledLine: SketchLineHelper = {
add: ({
@ -576,7 +576,7 @@ export const angledLine: SketchLineHelper = {
pathToNode,
}
},
addTag: addTag(),
addTag: addTagWithTo('angleLength'),
}
export const angledLineOfXLength: SketchLineHelper = {
@ -649,7 +649,7 @@ export const angledLineOfXLength: SketchLineHelper = {
pathToNode,
}
},
addTag: addTag(),
addTag: addTagWithTo('angleLength'),
}
export const angledLineOfYLength: SketchLineHelper = {
@ -723,7 +723,7 @@ export const angledLineOfYLength: SketchLineHelper = {
pathToNode,
}
},
addTag: addTag(),
addTag: addTagWithTo('angleLength'),
}
export const angledLineToX: SketchLineHelper = {
@ -792,7 +792,7 @@ export const angledLineToX: SketchLineHelper = {
pathToNode,
}
},
addTag: addTag(),
addTag: addTagWithTo('angleTo'),
}
export const angledLineToY: SketchLineHelper = {
@ -862,7 +862,7 @@ export const angledLineToY: SketchLineHelper = {
pathToNode,
}
},
addTag: addTag(),
addTag: addTagWithTo('angleTo'),
}
export const angledLineThatIntersects: SketchLineHelper = {
@ -951,7 +951,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
pathToNode,
}
},
addTag: addTag(), // TODO might be wrong
addTag: addTagWithTo('angleTo'), // TODO might be wrong
}
export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
@ -1174,31 +1174,29 @@ function isAngleLiteral(lineArugement: Value): boolean {
type addTagFn = (a: ModifyAstBase) => { modifiedAst: Program; tag: string }
function addTag(): addTagFn {
function addTagWithTo(
argType: 'angleLength' | 'angleTo' | 'default' | 'length'
): addTagFn {
return ({ node, pathToNode }) => {
let tagName = findUniqueName(node, 'seg', 2)
const _node = { ...node }
const { node: primaryCallExp } = getNodeFromPath<CallExpression>(
const { node: callExpression } = getNodeFromPath<CallExpression>(
_node,
pathToNode,
'CallExpression'
pathToNode
)
// Tag is always 3rd expression now, using arg index feels brittle
// but we can come up with a better way to identify tag later.
const thirdArg = primaryCallExp.arguments?.[2]
const tagLiteral =
thirdArg || (createLiteral(findUniqueName(_node, 'seg', 2)) as Literal)
const isTagExisting = !!thirdArg
if (!isTagExisting) {
primaryCallExp.arguments[2] = tagLiteral
}
if ('value' in tagLiteral) {
// Now TypeScript knows tagLiteral has a value property
const tagArg = callExpression.arguments?.[2]
if (tagArg) {
return {
modifiedAst: _node,
tag: String(tagLiteral.value),
tag: String(tagArg),
}
} else {
throw new Error('Unable to assign tag without value')
callExpression.arguments[2] = createLiteral(tagName)
return {
modifiedAst: _node,
tag: tagName,
}
}
}
}

1168
src/wasm-lib/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -59,7 +59,7 @@ members = [
]
[workspace.dependencies]
kittycad = { path = "../../../kittycad.rs/kittycad", default-features = false, features = ["js", "requests"] }
kittycad = { version = "0.2.60", default-features = false, features = ["js", "requests"] }
kittycad-execution-plan = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
kittycad-execution-plan-macros = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
kittycad-execution-plan-traits = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }

View File

@ -6,7 +6,6 @@ description = "A new executor for KCL which compiles to Execution Plans"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
image = { version = "0.24.7", default-features = false, features = ["png"] }
kcl-lib = { path = "../kcl" }
kittycad = { workspace = true }
kittycad-execution-plan = { workspace = true }
@ -16,7 +15,6 @@ kittycad-modeling-cmds = { workspace = true }
kittycad-modeling-session = { workspace = true }
thiserror = "1.0.57"
tokio = { version = "1.36.0", features = ["macros", "rt"] }
twenty-twenty = "0.7.0"
uuid = "1.7"
[dev-dependencies]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

View File

@ -2,16 +2,13 @@ use std::collections::HashMap;
use kcl_lib::ast::types::{LiteralIdentifier, LiteralValue};
use kittycad_execution_plan::constants;
use kittycad_execution_plan_traits::Primitive;
use super::{native_functions, Address};
use crate::{CompileError, KclFunction};
/// KCL values which can be written to KCEP memory.
/// This is recursive. For example, the bound value might be an array, which itself contains bound values.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub enum EpBinding {
/// A KCL value which gets stored in a particular address in KCEP memory.
Single(Address),
@ -26,8 +23,6 @@ pub enum EpBinding {
properties: HashMap<String, EpBinding>,
},
/// Not associated with a KCEP address.
Constant(Primitive),
/// Not associated with a KCEP address.
Function(KclFunction),
/// SketchGroups have their own storage.
SketchGroup { index: usize },
@ -57,13 +52,11 @@ impl EpBinding {
EpBinding::SketchGroup { .. } => Err(CompileError::CannotIndex),
EpBinding::Single(_) => Err(CompileError::CannotIndex),
EpBinding::Function(_) => Err(CompileError::CannotIndex),
EpBinding::Constant(_) => Err(CompileError::CannotIndex),
},
// Objects can be indexed by string properties.
LiteralValue::String(property) => match self {
EpBinding::Single(_) => Err(CompileError::NoProperties),
EpBinding::Function(_) => Err(CompileError::NoProperties),
EpBinding::Constant(_) => Err(CompileError::CannotIndex),
EpBinding::SketchGroup { .. } => Err(CompileError::NoProperties),
EpBinding::Sequence { .. } => Err(CompileError::ArrayDoesNotHaveProperties),
EpBinding::Map {
@ -110,58 +103,8 @@ impl BindingScope {
// TODO: Actually put the stdlib prelude in here,
// things like `startSketchAt` and `line`.
ep_bindings: HashMap::from([
("E".into(), EpBinding::Constant(constants::E)),
("PI".into(), EpBinding::Constant(constants::PI)),
("id".into(), EpBinding::from(KclFunction::Id(native_functions::Id))),
("abs".into(), EpBinding::from(KclFunction::Abs(native_functions::Abs))),
(
"acos".into(),
EpBinding::from(KclFunction::Acos(native_functions::Acos)),
),
(
"asin".into(),
EpBinding::from(KclFunction::Asin(native_functions::Asin)),
),
(
"atan".into(),
EpBinding::from(KclFunction::Atan(native_functions::Atan)),
),
(
"ceil".into(),
EpBinding::from(KclFunction::Ceil(native_functions::Ceil)),
),
("cos".into(), EpBinding::from(KclFunction::Cos(native_functions::Cos))),
(
"floor".into(),
EpBinding::from(KclFunction::Floor(native_functions::Floor)),
),
("ln".into(), EpBinding::from(KclFunction::Ln(native_functions::Ln))),
(
"log10".into(),
EpBinding::from(KclFunction::Log10(native_functions::Log10)),
),
(
"log2".into(),
EpBinding::from(KclFunction::Log2(native_functions::Log2)),
),
("sin".into(), EpBinding::from(KclFunction::Sin(native_functions::Sin))),
(
"sqrt".into(),
EpBinding::from(KclFunction::Sqrt(native_functions::Sqrt)),
),
("tan".into(), EpBinding::from(KclFunction::Tan(native_functions::Tan))),
(
"toDegrees".into(),
EpBinding::from(KclFunction::ToDegrees(native_functions::ToDegrees)),
),
(
"toRadians".into(),
EpBinding::from(KclFunction::ToRadians(native_functions::ToRadians)),
),
("add".into(), EpBinding::from(KclFunction::Add(native_functions::Add))),
("log".into(), EpBinding::from(KclFunction::Log(native_functions::Log))),
("max".into(), EpBinding::from(KclFunction::Max(native_functions::Max))),
("min".into(), EpBinding::from(KclFunction::Min(native_functions::Min))),
(
"startSketchAt".into(),
EpBinding::from(KclFunction::StartSketchAt(native_functions::sketch::StartSketchAt)),
@ -170,26 +113,6 @@ impl BindingScope {
"lineTo".into(),
EpBinding::from(KclFunction::LineTo(native_functions::sketch::LineTo)),
),
(
"line".into(),
EpBinding::from(KclFunction::Line(native_functions::sketch::Line)),
),
(
"xLineTo".into(),
EpBinding::from(KclFunction::XLineTo(native_functions::sketch::XLineTo)),
),
(
"xLine".into(),
EpBinding::from(KclFunction::XLine(native_functions::sketch::XLine)),
),
(
"yLineTo".into(),
EpBinding::from(KclFunction::YLineTo(native_functions::sketch::YLineTo)),
),
(
"yLine".into(),
EpBinding::from(KclFunction::YLine(native_functions::sketch::YLine)),
),
(
"extrude".into(),
EpBinding::from(KclFunction::Extrude(native_functions::sketch::Extrude)),

View File

@ -61,7 +61,7 @@ pub enum Error {
#[error("Failed on instruction {instruction_index}:\n{error}\n\nInstruction contents were {instruction:#?}")]
Execution {
error: ExecutionError,
instruction: Instruction,
instruction: Option<Instruction>,
instruction_index: usize,
},
}
@ -76,7 +76,7 @@ impl From<ExecutionFailed> for Error {
) -> Self {
Self::Execution {
error,
instruction: instruction.expect("no instruction"),
instruction,
instruction_index,
}
}

View File

@ -259,32 +259,9 @@ impl Planner {
binding,
} = match callee {
KclFunction::Id(f) => f.call(&mut ctx, args)?,
KclFunction::Abs(f) => f.call(&mut ctx, args)?,
KclFunction::Acos(f) => f.call(&mut ctx, args)?,
KclFunction::Asin(f) => f.call(&mut ctx, args)?,
KclFunction::Atan(f) => f.call(&mut ctx, args)?,
KclFunction::Ceil(f) => f.call(&mut ctx, args)?,
KclFunction::Cos(f) => f.call(&mut ctx, args)?,
KclFunction::Floor(f) => f.call(&mut ctx, args)?,
KclFunction::Ln(f) => f.call(&mut ctx, args)?,
KclFunction::Log10(f) => f.call(&mut ctx, args)?,
KclFunction::Log2(f) => f.call(&mut ctx, args)?,
KclFunction::Sin(f) => f.call(&mut ctx, args)?,
KclFunction::Sqrt(f) => f.call(&mut ctx, args)?,
KclFunction::Tan(f) => f.call(&mut ctx, args)?,
KclFunction::ToDegrees(f) => f.call(&mut ctx, args)?,
KclFunction::ToRadians(f) => f.call(&mut ctx, args)?,
KclFunction::Log(f) => f.call(&mut ctx, args)?,
KclFunction::Max(f) => f.call(&mut ctx, args)?,
KclFunction::Min(f) => f.call(&mut ctx, args)?,
KclFunction::StartSketchAt(f) => f.call(&mut ctx, args)?,
KclFunction::Extrude(f) => f.call(&mut ctx, args)?,
KclFunction::LineTo(f) => f.call(&mut ctx, args)?,
KclFunction::Line(f) => f.call(&mut ctx, args)?,
KclFunction::XLineTo(f) => f.call(&mut ctx, args)?,
KclFunction::XLine(f) => f.call(&mut ctx, args)?,
KclFunction::YLineTo(f) => f.call(&mut ctx, args)?,
KclFunction::YLine(f) => f.call(&mut ctx, args)?,
KclFunction::Add(f) => f.call(&mut ctx, args)?,
KclFunction::Close(f) => f.call(&mut ctx, args)?,
KclFunction::UserDefined(f) => {
@ -652,32 +629,9 @@ impl Eq for UserDefinedFunction {}
#[cfg_attr(test, derive(Eq, PartialEq))]
enum KclFunction {
Id(native_functions::Id),
Abs(native_functions::Abs),
Acos(native_functions::Acos),
Asin(native_functions::Asin),
Atan(native_functions::Atan),
Ceil(native_functions::Ceil),
Cos(native_functions::Cos),
Floor(native_functions::Floor),
Ln(native_functions::Ln),
Log10(native_functions::Log10),
Log2(native_functions::Log2),
Sin(native_functions::Sin),
Sqrt(native_functions::Sqrt),
Tan(native_functions::Tan),
ToDegrees(native_functions::ToDegrees),
ToRadians(native_functions::ToRadians),
StartSketchAt(native_functions::sketch::StartSketchAt),
LineTo(native_functions::sketch::LineTo),
Line(native_functions::sketch::Line),
XLineTo(native_functions::sketch::XLineTo),
XLine(native_functions::sketch::XLine),
YLineTo(native_functions::sketch::YLineTo),
YLine(native_functions::sketch::YLine),
Add(native_functions::Add),
Log(native_functions::Log),
Max(native_functions::Max),
Min(native_functions::Min),
UserDefined(UserDefinedFunction),
Extrude(native_functions::sketch::Extrude),
Close(native_functions::sketch::Close),

View File

@ -2,15 +2,18 @@
//! This includes some of the stdlib, e.g. `startSketchAt`.
//! But some other stdlib functions will be written in KCL.
use kittycad_execution_plan::{
BinaryArithmetic, BinaryOperation, Destination, Instruction, Operand, UnaryArithmetic, UnaryOperation,
};
use kittycad_execution_plan::{BinaryArithmetic, Destination, Instruction};
use kittycad_execution_plan_traits::Address;
use crate::{CompileError, EpBinding, EvalPlan};
pub mod sketch;
/// The identity function. Always returns its first input.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct Id;
pub trait Callable {
fn call(&self, ctx: &mut Context<'_>, args: Vec<EpBinding>) -> Result<EvalPlan, CompileError>;
}
@ -29,65 +32,6 @@ impl<'a> Context<'a> {
}
}
/// Unary operator macro to quickly create new bindings.
macro_rules! define_unary {
() => {};
($h:ident$( $r:ident)*) => {
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct $h;
impl Callable for $h {
fn call(&self, ctx: &mut Context<'_>, mut args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
if args.len() > 1 {
return Err(CompileError::TooManyArgs {
fn_name: "$h".into(),
maximum: 1,
actual: args.len(),
});
}
let not_enough_args = CompileError::NotEnoughArgs {
fn_name: "$h".into(),
required: 1,
actual: args.len(),
};
let EpBinding::Single(arg0) = args.pop().ok_or(not_enough_args.clone())? else {
return Err(CompileError::InvalidOperand("A single value binding is expected"));
};
let destination = ctx.next_address.offset_by(1);
let instructions = vec![
Instruction::UnaryArithmetic {
arithmetic: UnaryArithmetic {
operation: UnaryOperation::$h,
operand: Operand::Reference(arg0)
},
destination: Destination::Address(destination)
}
];
Ok(EvalPlan {
instructions,
binding: EpBinding::Single(destination),
})
}
}
define_unary!($($r)*);
};
}
define_unary!(Abs Acos Asin Atan Ceil Cos Floor Ln Log10 Log2 Sin Sqrt Tan ToDegrees ToRadians);
/// The identity function. Always returns its first input.
/// Implemented purely on the KCL side so it doesn't need to be in the
/// define_unary! macro above.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct Id;
impl Callable for Id {
fn call(&self, _: &mut Context<'_>, args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
if args.len() > 1 {
@ -112,53 +56,44 @@ impl Callable for Id {
}
}
/// Binary operator macro to quickly create new bindings.
macro_rules! define_binary {
() => {};
($h:ident$( $r:ident)*) => {
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct $h;
/// A test function that adds two numbers.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct Add;
impl Callable for $h {
fn call(&self, ctx: &mut Context<'_>, mut args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
let len = args.len();
if len > 2 {
return Err(CompileError::TooManyArgs {
fn_name: "$h".into(),
maximum: 2,
actual: len,
});
}
let not_enough_args = CompileError::NotEnoughArgs {
fn_name: "$h".into(),
required: 2,
actual: len,
};
const ERR: &str = "cannot use composite values (e.g. array) as arguments to $h";
let EpBinding::Single(arg1) = args.pop().ok_or(not_enough_args.clone())? else {
return Err(CompileError::InvalidOperand(ERR));
};
let EpBinding::Single(arg0) = args.pop().ok_or(not_enough_args)? else {
return Err(CompileError::InvalidOperand(ERR));
};
let destination = ctx.next_address.offset_by(1);
Ok(EvalPlan {
instructions: vec![Instruction::BinaryArithmetic {
arithmetic: BinaryArithmetic {
operation: BinaryOperation::$h,
operand0: Operand::Reference(arg0),
operand1: Operand::Reference(arg1),
},
destination: Destination::Address(destination),
}],
binding: EpBinding::Single(destination),
})
impl Callable for Add {
fn call(&self, ctx: &mut Context<'_>, mut args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
let len = args.len();
if len > 2 {
return Err(CompileError::TooManyArgs {
fn_name: "add".into(),
maximum: 2,
actual: len,
});
}
let not_enough_args = CompileError::NotEnoughArgs {
fn_name: "add".into(),
required: 2,
actual: len,
};
const ERR: &str = "cannot use composite values (e.g. array) as arguments to Add";
let EpBinding::Single(arg1) = args.pop().ok_or(not_enough_args.clone())? else {
return Err(CompileError::InvalidOperand(ERR));
};
let EpBinding::Single(arg0) = args.pop().ok_or(not_enough_args)? else {
return Err(CompileError::InvalidOperand(ERR));
};
let destination = ctx.next_address.offset_by(1);
Ok(EvalPlan {
instructions: vec![Instruction::BinaryArithmetic {
arithmetic: BinaryArithmetic {
operation: kittycad_execution_plan::BinaryOperation::Add,
operand0: kittycad_execution_plan::Operand::Reference(arg0),
operand1: kittycad_execution_plan::Operand::Reference(arg1),
},
destination: Destination::Address(destination),
}],
binding: EpBinding::Single(destination),
})
}
define_binary!($($r)*);
};
}
define_binary!(Add Log Max Min);

View File

@ -3,4 +3,4 @@
pub mod helpers;
pub mod stdlib_functions;
pub use stdlib_functions::{Close, Extrude, Line, LineTo, StartSketchAt, XLine, XLineTo, YLine, YLineTo};
pub use stdlib_functions::{Close, Extrude, LineTo, StartSketchAt};

View File

@ -67,12 +67,6 @@ pub fn sg_binding(
actual: "function".to_owned(),
arg_number,
}),
EpBinding::Constant(_) => Err(CompileError::ArgWrongType {
fn_name,
expected,
actual: "constant".to_owned(),
arg_number,
}),
}
}
pub fn single_binding(
@ -107,12 +101,6 @@ pub fn single_binding(
actual: "function".to_owned(),
arg_number,
}),
EpBinding::Constant(_) => Err(CompileError::ArgWrongType {
fn_name,
expected,
actual: "constant".to_owned(),
arg_number,
}),
}
}
@ -148,16 +136,10 @@ pub fn sequence_binding(
actual: "function".to_owned(),
arg_number,
}),
EpBinding::Constant(_) => Err(CompileError::ArgWrongType {
fn_name,
expected,
actual: "constant".to_owned(),
arg_number,
}),
}
}
/// Extract a 2D point from an argument to a KCL Function.
/// Extract a 2D point from an argument to a Cabble.
pub fn arg_point2d(
arg: EpBinding,
fn_name: &'static str,
@ -166,7 +148,7 @@ pub fn arg_point2d(
arg_number: usize,
) -> Result<Address, CompileError> {
let expected = "2D point (array with length 2)";
let elements = sequence_binding(arg, fn_name, "an array of length 2", arg_number)?;
let elements = sequence_binding(arg, "startSketchAt", "an array of length 2", arg_number)?;
if elements.len() != 2 {
return Err(CompileError::ArgWrongType {
fn_name,
@ -183,12 +165,12 @@ pub fn arg_point2d(
let start_z = start + 2;
instructions.extend([
Instruction::Copy {
source: single_binding(elements[0].clone(), fn_name, "number", arg_number)?,
source: single_binding(elements[0].clone(), "startSketchAt", "number", arg_number)?,
destination: Destination::Address(start_x),
length: 1,
},
Instruction::Copy {
source: single_binding(elements[1].clone(), fn_name, "number", arg_number)?,
source: single_binding(elements[1].clone(), "startSketchAt", "number", arg_number)?,
destination: Destination::Address(start_y),
length: 1,
},

View File

@ -1,7 +1,7 @@
use kittycad_execution_plan::{
api_request::ApiRequest,
sketch_types::{self, Axes, BasePath, Plane, SketchGroup},
BinaryArithmetic, BinaryOperation, Destination, Instruction, Operand,
Destination, Instruction,
};
use kittycad_execution_plan_traits::{Address, InMemory, Primitive, Value};
use kittycad_modeling_cmds::{
@ -13,22 +13,6 @@ use uuid::Uuid;
use super::helpers::{arg_point2d, no_arg_api_call, sg_binding, single_binding, stack_api_call};
use crate::{binding_scope::EpBinding, error::CompileError, native_functions::Callable, EvalPlan};
#[derive(PartialEq)]
pub enum At {
RelativeXY,
AbsoluteXY,
RelativeX,
AbsoluteX,
RelativeY,
AbsoluteY,
}
impl At {
pub fn is_relative(&self) -> bool {
*self == At::RelativeX || *self == At::RelativeY || *self == At::RelativeXY
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct Close;
@ -156,124 +140,25 @@ impl Callable for LineTo {
&self,
ctx: &mut crate::native_functions::Context<'_>,
args: Vec<EpBinding>,
) -> Result<EvalPlan, CompileError> {
LineBare::call(ctx, "lineTo", args, LineBareOptions { at: At::AbsoluteXY })
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct Line;
impl Callable for Line {
fn call(
&self,
ctx: &mut crate::native_functions::Context<'_>,
args: Vec<EpBinding>,
) -> Result<EvalPlan, CompileError> {
LineBare::call(ctx, "line", args, LineBareOptions { at: At::RelativeXY })
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct XLineTo;
impl Callable for XLineTo {
fn call(
&self,
ctx: &mut crate::native_functions::Context<'_>,
args: Vec<EpBinding>,
) -> Result<EvalPlan, CompileError> {
LineBare::call(ctx, "xLineTo", args, LineBareOptions { at: At::AbsoluteX })
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct XLine;
impl Callable for XLine {
fn call(
&self,
ctx: &mut crate::native_functions::Context<'_>,
args: Vec<EpBinding>,
) -> Result<EvalPlan, CompileError> {
LineBare::call(ctx, "xLine", args, LineBareOptions { at: At::RelativeX })
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct YLineTo;
impl Callable for YLineTo {
fn call(
&self,
ctx: &mut crate::native_functions::Context<'_>,
args: Vec<EpBinding>,
) -> Result<EvalPlan, CompileError> {
LineBare::call(ctx, "yLineTo", args, LineBareOptions { at: At::AbsoluteY })
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct YLine;
impl Callable for YLine {
fn call(
&self,
ctx: &mut crate::native_functions::Context<'_>,
args: Vec<EpBinding>,
) -> Result<EvalPlan, CompileError> {
LineBare::call(ctx, "yLine", args, LineBareOptions { at: At::RelativeY })
}
}
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
/// Exposes all the possible arguments the `line` modeling command can take.
/// Reduces code for the other line functions needed.
/// We do not expose this to the developer since it does not align with
/// the documentation (there is no "lineBare").
pub struct LineBare;
/// Used to configure the call to handle different line variants.
pub struct LineBareOptions {
/// Where to start coordinates at, ex: At::RelativeXY.
at: At,
}
impl LineBare {
fn call(
ctx: &mut crate::native_functions::Context<'_>,
fn_name: &'static str,
args: Vec<EpBinding>,
opts: LineBareOptions,
) -> Result<EvalPlan, CompileError> {
let mut instructions = Vec::new();
let required = 2;
let fn_name = "lineTo";
// Get both required params.
let mut args_iter = args.into_iter();
let Some(to) = args_iter.next() else {
return Err(CompileError::NotEnoughArgs {
fn_name: fn_name.into(),
required,
actual: args_iter.count(),
required: 2,
actual: 0,
});
};
let Some(sketch_group) = args_iter.next() else {
return Err(CompileError::NotEnoughArgs {
fn_name: fn_name.into(),
required,
actual: args_iter.count(),
required: 2,
actual: 1,
});
};
let tag = match args_iter.next() {
Some(a) => a,
None => {
@ -286,90 +171,26 @@ impl LineBare {
EpBinding::Single(empty_string_addr)
}
};
// Check the type of required params.
// We don't check `to` here because it can take on either a
// EpBinding::Sequence or EpBinding::Single.
let to = arg_point2d(to, fn_name, &mut instructions, ctx, 0)?;
let sg = sg_binding(sketch_group, fn_name, "sketch group", 1)?;
let tag = single_binding(tag, fn_name, "string tag", 2)?;
let id = Uuid::new_v4();
// Start of the path segment (which is a straight line).
let length_of_3d_point = Point3d::<f64>::default().into_parts().len();
let start_of_line = ctx.next_address.offset_by(1);
// Reserve space for the line's end, and the `relative: bool` field.
ctx.next_address.offset_by(length_of_3d_point + 1);
let new_sg_index = ctx.assign_sketch_group();
// Copy based on the options.
match opts {
LineBareOptions { at: At::AbsoluteXY, .. } | LineBareOptions { at: At::RelativeXY, .. } => {
// Push the `to` 2D point onto the stack.
let EpBinding::Sequence { elements, length_at: _ } = to.clone() else {
return Err(CompileError::InvalidOperand("Must pass a list of length 2"));
};
let &[EpBinding::Single(el0), EpBinding::Single(el1)] = elements.as_slice() else {
return Err(CompileError::InvalidOperand("Must pass a sequence here."));
};
instructions.extend([
Instruction::Copy {
// X
source: el0,
length: 1,
destination: Destination::StackPush,
},
Instruction::Copy {
// Y
source: el1,
length: 1,
destination: Destination::StackExtend,
},
Instruction::StackExtend { data: vec![0.0.into()] }, // Z
]);
}
LineBareOptions { at: At::AbsoluteX, .. } | LineBareOptions { at: At::RelativeX, .. } => {
let EpBinding::Single(addr) = to else {
return Err(CompileError::InvalidOperand("Must pass a single value here."));
};
instructions.extend([
Instruction::Copy {
// X
source: addr,
length: 1,
destination: Destination::StackPush,
},
Instruction::StackExtend {
data: vec![Primitive::from(0.0)],
}, // Y
Instruction::StackExtend {
data: vec![Primitive::from(0.0)],
}, // Z
]);
}
LineBareOptions { at: At::AbsoluteY, .. } | LineBareOptions { at: At::RelativeY, .. } => {
let EpBinding::Single(addr) = to else {
return Err(CompileError::InvalidOperand("Must pass a single value here."));
};
instructions.extend([
Instruction::StackPush {
data: vec![Primitive::from(0.0)],
}, // X
Instruction::Copy {
// Y
source: addr,
length: 1,
destination: Destination::StackExtend,
},
Instruction::StackExtend {
data: vec![Primitive::from(0.0)],
}, // Z
]);
}
}
instructions.extend([
// Push the `to` 2D point onto the stack.
Instruction::Copy {
source: to,
length: 2,
destination: Destination::StackPush,
},
// Make it a 3D point.
Instruction::StackExtend { data: vec![0.0.into()] },
// Append the new path segment to memory.
// First comes its tag.
Instruction::SetPrimitive {
@ -383,7 +204,7 @@ impl LineBare {
// Then its `relative` field.
Instruction::SetPrimitive {
address: start_of_line + 1 + length_of_3d_point,
value: opts.at.is_relative().into(),
value: false.into(),
},
// Push the path ID onto the stack.
Instruction::SketchGroupCopyFrom {
@ -410,159 +231,16 @@ impl LineBare {
data: vec![Primitive::from("ToPoint".to_owned())],
},
// `BasePath::from` point.
// Place them in the secondary stack to prepare ToPoint structure.
Instruction::SketchGroupGetLastPoint {
source: sg,
destination: Destination::StackExtend,
},
]);
// Reserve space for the segment last point
let to_point_from = ctx.next_address.offset_by(2);
instructions.extend([
// Copy to the primary stack as well to be worked with.
Instruction::SketchGroupGetLastPoint {
source: sg,
destination: Destination::Address(to_point_from),
// `BasePath::to` point.
Instruction::Copy {
source: start_of_line + 1,
length: 2,
destination: Destination::StackExtend,
},
]);
// `BasePath::to` point.
// The copy here depends on the incoming `to` data.
// Sometimes it's a list, sometimes it's single datum.
// And the relative/not relative matters. When relative, we need to
// copy coords from `from` into the new `to` coord that don't change.
// At least everything else can be built up from these "primitives".
if let EpBinding::Sequence { elements, length_at: _ } = to.clone() {
if let &[EpBinding::Single(el0), EpBinding::Single(el1)] = elements.as_slice() {
match opts {
// ToPoint { from: { x1, y1 }, to: { x1 + x2, y1 + y2 } }
LineBareOptions { at: At::RelativeXY, .. } => {
instructions.extend([
Instruction::BinaryArithmetic {
arithmetic: BinaryArithmetic {
operation: BinaryOperation::Add,
operand0: Operand::Reference(to_point_from + 0),
operand1: Operand::Reference(el0),
},
destination: Destination::StackExtend,
},
Instruction::BinaryArithmetic {
arithmetic: BinaryArithmetic {
operation: BinaryOperation::Add,
operand0: Operand::Reference(to_point_from + 1),
operand1: Operand::Reference(el1),
},
destination: Destination::StackExtend,
},
]);
}
// ToPoint { from: { x1, y1 }, to: { x2, y2 } }
LineBareOptions { at: At::AbsoluteXY, .. } => {
// Otherwise just directly copy the new points.
instructions.extend([
Instruction::Copy {
source: el0,
length: 1,
destination: Destination::StackExtend,
},
Instruction::Copy {
source: el1,
length: 1,
destination: Destination::StackExtend,
},
]);
}
_ => {
return Err(CompileError::InvalidOperand(
"A Sequence with At::...X or At::...Y is not valid here. Must be At::...XY.",
));
}
}
}
} else if let EpBinding::Single(addr) = to {
match opts {
// ToPoint { from: { x1, y1 }, to: { x1 + x2, y1 } }
LineBareOptions { at: At::RelativeX } => {
instructions.extend([
Instruction::BinaryArithmetic {
arithmetic: BinaryArithmetic {
operation: BinaryOperation::Add,
operand0: Operand::Reference(to_point_from + 0),
operand1: Operand::Reference(addr),
},
destination: Destination::StackExtend,
},
Instruction::Copy {
source: to_point_from + 1,
length: 1,
destination: Destination::StackExtend,
},
]);
}
// ToPoint { from: { x1, y1 }, to: { x2, y1 } }
LineBareOptions { at: At::AbsoluteX } => {
instructions.extend([
Instruction::Copy {
source: addr,
length: 1,
destination: Destination::StackExtend,
},
Instruction::Copy {
source: to_point_from + 1,
length: 1,
destination: Destination::StackExtend,
},
]);
}
// ToPoint { from: { x1, y1 }, to: { x1, y1 + y2 } }
LineBareOptions { at: At::RelativeY } => {
instructions.extend([
Instruction::Copy {
source: to_point_from + 0,
length: 1,
destination: Destination::StackExtend,
},
Instruction::BinaryArithmetic {
arithmetic: BinaryArithmetic {
operation: BinaryOperation::Add,
operand0: Operand::Reference(to_point_from + 1),
operand1: Operand::Reference(addr),
},
destination: Destination::StackExtend,
},
]);
}
// ToPoint { from: { x1, y1 }, to: { x1, y2 } }
LineBareOptions { at: At::AbsoluteY } => {
instructions.extend([
Instruction::Copy {
source: to_point_from + 0,
length: 1,
destination: Destination::StackExtend,
},
Instruction::Copy {
source: addr,
length: 1,
destination: Destination::StackExtend,
},
]);
}
_ => {
return Err(CompileError::InvalidOperand(
"A Single binding with At::...XY is not valid here.",
));
}
}
} else {
return Err(CompileError::InvalidOperand(
"Must be a sequence or single value binding.",
));
}
instructions.extend([
// `BasePath::name` string.
Instruction::Copy {
source: tag,

View File

@ -1,7 +1,7 @@
use std::{collections::HashMap, env};
use ep::{constants, sketch_types, Destination, UnaryArithmetic};
use ept::{ListHeader, ObjectHeader, Primitive};
use ep::{sketch_types, Destination, UnaryArithmetic};
use ept::{ListHeader, ObjectHeader};
use kittycad_modeling_cmds::shared::Point2d;
use kittycad_modeling_session::SessionBuilder;
use pretty_assertions::assert_eq;
@ -1048,10 +1048,42 @@ fn store_object_with_array_property() {
/// Write the program's plan to the KCVM debugger's normal input file.
#[allow(unused)]
fn kcvm_dbg(kcl_program: &str, path: &str) {
fn kcvm_dbg(kcl_program: &str) {
let (plan, _scope, _) = must_plan(kcl_program);
let plan_json = serde_json::to_string_pretty(&plan).unwrap();
std::fs::write(path, plan_json).unwrap();
std::fs::write(
"/Users/adamchalmers/kc-repos/modeling-api/execution-plan-debugger/test_input.json",
plan_json,
)
.unwrap();
}
#[tokio::test]
async fn paul_qr_test() {
let program = include_str!("../testdata/paul_qr.kcl");
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
.ast()
.unwrap();
let mut client = Some(test_client().await);
crate::execute(ast, &mut client).await.unwrap();
use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat};
let out = client
.unwrap()
.run_command(
uuid::Uuid::new_v4().into(),
each_cmd::TakeSnapshot {
format: ImageFormat::Png,
}
.into(),
)
.await
.unwrap();
let out = match out {
OkModelingCmdResponse::TakeSnapshot(b) => b,
other => panic!("wrong output: {other:?}"),
};
let out: Vec<u8> = out.contents.into();
std::fs::write("paul_qr_code.png", out).unwrap();
}
#[tokio::test]
@ -1065,6 +1097,8 @@ async fn stdlib_cube_partial() {
|> close(%)
|> extrude(100.0, %)
"#;
let (_plan, _scope, last_address) = must_plan(program);
assert_eq!(last_address, Address::ZERO + 66);
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
.ast()
.unwrap();
@ -1107,115 +1141,23 @@ async fn stdlib_cube_partial() {
},
]
);
use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat};
let out = client
.unwrap()
.run_command(
uuid::Uuid::new_v4().into(),
kittycad_modeling_cmds::ModelingCmd::from(each_cmd::TakeSnapshot {
format: ImageFormat::Png,
}),
)
.await
.unwrap();
let out = match out {
OkModelingCmdResponse::TakeSnapshot(kittycad_modeling_cmds::output::TakeSnapshot { contents: b }) => b,
other => panic!("wrong output: {other:?}"),
};
use image::io::Reader as ImageReader;
let img = ImageReader::new(std::io::Cursor::new(out))
.with_guessed_format()
.unwrap()
.decode()
.unwrap();
twenty_twenty::assert_image("fixtures/cube_lineTo.png", &img, 0.9999);
}
#[tokio::test]
async fn stdlib_cube_xline_yline() {
let program = r#"
let cube = startSketchAt([0.0, 0.0], "adam")
|> xLine(210.0, %, "side0")
|> yLine(210.0, %, "side1")
|> xLine(-210.0, %, "side2")
|> yLine(-210.0, %, "side3")
|> close(%)
|> extrude(100.0, %)
"#;
kcvm_dbg(
program,
"/home/lee/Code/Zoo/modeling-api/execution-plan-debugger/cube_xyline.json",
);
let (_plan, _scope, _last_address) = must_plan(program);
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
.ast()
.unwrap();
let mut client = Some(test_client().await);
let mem = match crate::execute(ast, &mut client).await {
Ok(mem) => mem,
Err(e) => panic!("{e}"),
};
let sg = &mem.sketch_groups.last().unwrap();
assert_eq!(
sg.path_rest,
vec![
sketch_types::PathSegment::ToPoint {
base: sketch_types::BasePath {
from: Point2d { x: 0.0, y: 0.0 },
to: Point2d { x: 210.0, y: 0.0 },
name: "side0".into(),
}
},
sketch_types::PathSegment::ToPoint {
base: sketch_types::BasePath {
from: Point2d { x: 210.0, y: 0.0 },
to: Point2d { x: 210.0, y: 210.0 },
name: "side1".into(),
}
},
sketch_types::PathSegment::ToPoint {
base: sketch_types::BasePath {
from: Point2d { x: 210.0, y: 210.0 },
to: Point2d { x: 0.0, y: 210.0 },
name: "side2".into(),
}
},
sketch_types::PathSegment::ToPoint {
base: sketch_types::BasePath {
from: Point2d { x: 0.0, y: 210.0 },
to: Point2d { x: 0.0, y: 0.0 },
name: "side3".into(),
}
},
]
);
use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat};
let out = client
.unwrap()
.run_command(
uuid::Uuid::new_v4().into(),
kittycad_modeling_cmds::ModelingCmd::from(each_cmd::TakeSnapshot {
format: ImageFormat::Png,
}),
)
.await
.unwrap();
let out = match out {
OkModelingCmdResponse::TakeSnapshot(kittycad_modeling_cmds::output::TakeSnapshot { contents: b }) => b,
other => panic!("wrong output: {other:?}"),
};
use image::io::Reader as ImageReader;
let img = ImageReader::new(std::io::Cursor::new(out))
.with_guessed_format()
.unwrap()
.decode()
.unwrap();
twenty_twenty::assert_image("fixtures/cube_xyLine.png", &img, 0.9999);
// use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat};
// let out = client
// .unwrap()
// .run_command(
// uuid::Uuid::new_v4().into(),
// each_cmd::TakeSnapshot {
// format: ImageFormat::Png,
// },
// )
// .await
// .unwrap();
// let out = match out {
// OkModelingCmdResponse::TakeSnapshot(b) => b,
// other => panic!("wrong output: {other:?}"),
// };
// let out: Vec<u8> = out.contents.into();
// std::fs::write("image.png", out).unwrap();
}
async fn test_client() -> Session {
@ -1414,31 +1356,3 @@ fn mod_and_pow() {
]
);
}
#[tokio::test]
async fn cos_sin_pi() {
let program = "
let x = cos(45.0)*10
let y = sin(45.0)*10
let z = PI
";
let (_plan, scope, _) = must_plan(program);
let Some(EpBinding::Single(x)) = scope.get("x") else {
panic!("Unexpected binding for variable 'x': {:?}", scope.get("x"));
};
let Some(EpBinding::Single(y)) = scope.get("y") else {
panic!("Unexpected binding for variable 'y': {:?}", scope.get("y"));
};
let Some(EpBinding::Constant(z)) = scope.get("z") else {
panic!("Unexpected binding for variable 'z': {:?}", scope.get("z"));
};
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
.ast()
.unwrap();
let mem = crate::execute(ast, &mut None).await.unwrap();
use ept::ReadMemory;
assert_eq!(*mem.get(x).unwrap(), Primitive::from(5.253219888177298));
assert_eq!(*mem.get(y).unwrap(), Primitive::from(8.509035245341185));
// Constants don't live in memory.
assert_eq!(*z, constants::PI);
}

2655
src/wasm-lib/grackle/testdata/paul_qr.kcl vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,6 @@ keywords = ["kcl", "KittyCAD", "CAD"]
anyhow = { version = "1.0.81", features = ["backtrace"] }
async-recursion = "1.0.5"
async-trait = "0.1.77"
boxcar = "0.2.4"
chrono = "0.4.35"
clap = { version = "4.5.2", features = ["cargo", "derive", "env", "unicode"], optional = true }
dashmap = "5.5.3"

View File

@ -7,10 +7,6 @@ name = "Inflector"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
dependencies = [
"lazy_static",
"regex",
]
[[package]]
name = "addr2line"
@ -48,26 +44,11 @@ dependencies = [
"memchr",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.81"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
dependencies = [
"backtrace",
]
@ -111,7 +92,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -122,7 +103,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -287,17 +268,12 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.35"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a"
checksum = "d87d9d13be47a5b7c3907137f1290b0459a7f80efb26be8c52afb11963bccb02"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets 0.52.0",
]
[[package]]
@ -344,21 +320,6 @@ dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "crypto-common"
version = "0.1.6"
@ -407,7 +368,7 @@ checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
"synstructure",
]
@ -419,11 +380,10 @@ checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946"
[[package]]
name = "derive-docs"
version = "0.1.11"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4e18a04fe569a325dd50743821f2605057ff5c4b48e60512270b4406907825b"
checksum = "834580a8bd697658876ed8c9f7727e49f01d34f5b859ca921ac5b99ffc6adf77"
dependencies = [
"Inflector",
"convert_case",
"once_cell",
"proc-macro2",
@ -431,7 +391,7 @@ dependencies = [
"regex",
"serde",
"serde_tokenstream",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -561,7 +521,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -632,7 +592,7 @@ dependencies = [
"inflections",
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -779,29 +739,6 @@ dependencies = [
"tokio-rustls 0.24.1",
]
[[package]]
name = "iana-time-zone"
version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "idna"
version = "0.4.0"
@ -870,23 +807,22 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.69"
version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kcl-lib"
version = "0.1.46"
version = "0.1.42"
dependencies = [
"anyhow",
"approx",
"async-recursion",
"async-trait",
"bson",
"chrono",
"dashmap",
"databake",
"derive-docs",
@ -897,14 +833,12 @@ dependencies = [
"kittycad-execution-plan-macros",
"kittycad-execution-plan-traits",
"lazy_static",
"mime_guess",
"parse-display 0.9.0",
"reqwest",
"ropey",
"schemars",
"serde",
"serde_json",
"sha2",
"thiserror",
"tokio",
"tokio-tungstenite",
@ -915,7 +849,6 @@ dependencies = [
"wasm-bindgen-futures",
"web-sys",
"winnow",
"zip",
]
[[package]]
@ -928,9 +861,9 @@ dependencies = [
[[package]]
name = "kittycad"
version = "0.2.60"
version = "0.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aa5906d0730bd90f6b3331fe57c04951d00743169a29ee96408767b4060605"
checksum = "13958174d876353f429ea8230dc92fe86f164819cea2e51bbf22e01a4c2a496e"
dependencies = [
"anyhow",
"async-trait",
@ -944,7 +877,6 @@ dependencies = [
"http 0.2.9",
"itertools",
"log",
"mime_guess",
"parse-display 0.8.2",
"phonenumber",
"rand",
@ -967,13 +899,14 @@ source = "git+https://github.com/KittyCAD/modeling-api?branch=main#1e6ef9601686c
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
name = "kittycad-execution-plan-traits"
version = "0.1.13"
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#494225aaac06fab77c4822e7dc48738ecca35392"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3ec8efd57b59697eb140b63c0ffe7db44fdfe5a55f14e45513411eba2280ba5"
dependencies = [
"serde",
"thiserror",
@ -1095,9 +1028,9 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.11"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
dependencies = [
"libc",
"wasi",
@ -1238,7 +1171,7 @@ dependencies = [
"regex",
"regex-syntax 0.7.5",
"structmeta 0.2.0",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -1252,7 +1185,7 @@ dependencies = [
"regex",
"regex-syntax 0.8.2",
"structmeta 0.3.0",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -1299,7 +1232,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -1471,9 +1404,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "reqwest"
version = "0.11.27"
version = "0.11.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251"
dependencies = [
"base64 0.21.3",
"bytes",
@ -1759,7 +1692,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -1793,7 +1726,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -1805,7 +1738,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -1843,17 +1776,6 @@ dependencies = [
"digest",
]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
@ -1925,7 +1847,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.2.0",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -1937,7 +1859,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.3.0",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -1948,7 +1870,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -1959,7 +1881,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -2003,9 +1925,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.53"
version = "2.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032"
checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb"
dependencies = [
"proc-macro2",
"quote",
@ -2026,7 +1948,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -2082,7 +2004,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -2155,7 +2077,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -2261,7 +2183,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -2290,7 +2212,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
]
[[package]]
@ -2328,7 +2250,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
"termcolor",
]
@ -2460,9 +2382,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -2470,24 +2392,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.42"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97"
dependencies = [
"cfg-if",
"js-sys",
@ -2497,9 +2419,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -2507,22 +2429,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.50",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
[[package]]
name = "wasm-streams"
@ -2539,9 +2461,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.69"
version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446"
dependencies = [
"js-sys",
"wasm-bindgen",
@ -2584,15 +2506,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
@ -2767,14 +2680,3 @@ name = "zeroize"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
[[package]]
name = "zip"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
dependencies = [
"byteorder",
"crc32fast",
"crossbeam-utils",
]

View File

@ -75,7 +75,6 @@ pub async fn modify_ast_for_sketch(
// Let's get the path info.
let resp = engine
.send_modeling_cmd(
false,
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::PathGetInfo { path_id: sketch_id },
@ -100,7 +99,6 @@ pub async fn modify_ast_for_sketch(
for segment in &path_info.segments {
if let Some(command_id) = &segment.command_id {
let h = engine.send_modeling_cmd(
false,
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::CurveGetControlPoints { curve_id: *command_id },

View File

@ -29,7 +29,6 @@ pub struct EngineConnection {
responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>>,
tcp_read_handle: Arc<TcpReadHandle>,
socket_health: Arc<Mutex<SocketHealth>>,
batch: Arc<Mutex<Vec<WebSocketRequest>>>,
}
pub struct TcpRead {
@ -155,136 +154,27 @@ impl EngineConnection {
}),
responses,
socket_health,
batch: Arc::new(Mutex::new(Vec::new())),
})
}
}
fn is_cmd_with_return_values(cmd: &kittycad::types::ModelingCmd) -> bool {
let (kittycad::types::ModelingCmd::Export { .. }
| kittycad::types::ModelingCmd::Extrude { .. }
| kittycad::types::ModelingCmd::SketchModeDisable { .. }
| kittycad::types::ModelingCmd::ObjectBringToFront { .. }
| kittycad::types::ModelingCmd::SelectWithPoint { .. }
| kittycad::types::ModelingCmd::HighlightSetEntity { .. }
| kittycad::types::ModelingCmd::EntityGetChildUuid { .. }
| kittycad::types::ModelingCmd::EntityGetNumChildren { .. }
| kittycad::types::ModelingCmd::EntityGetParentId { .. }
| kittycad::types::ModelingCmd::EntityGetAllChildUuids { .. }
| kittycad::types::ModelingCmd::CameraDragMove { .. }
| kittycad::types::ModelingCmd::CameraDragEnd { .. }
| kittycad::types::ModelingCmd::DefaultCameraGetSettings { .. }
| kittycad::types::ModelingCmd::DefaultCameraZoom { .. }
| kittycad::types::ModelingCmd::SelectGet { .. }
| kittycad::types::ModelingCmd::Solid3DGetAllEdgeFaces { .. }
| kittycad::types::ModelingCmd::Solid3DGetAllOppositeEdges { .. }
| kittycad::types::ModelingCmd::Solid3DGetOppositeEdge { .. }
| kittycad::types::ModelingCmd::Solid3DGetNextAdjacentEdge { .. }
| kittycad::types::ModelingCmd::Solid3DGetPrevAdjacentEdge { .. }
| kittycad::types::ModelingCmd::GetEntityType { .. }
| kittycad::types::ModelingCmd::CurveGetControlPoints { .. }
| kittycad::types::ModelingCmd::CurveGetType { .. }
| kittycad::types::ModelingCmd::MouseClick { .. }
| kittycad::types::ModelingCmd::TakeSnapshot { .. }
| kittycad::types::ModelingCmd::PathGetInfo { .. }
| kittycad::types::ModelingCmd::PathGetCurveUuidsForVertices { .. }
| kittycad::types::ModelingCmd::PathGetVertexUuids { .. }
| kittycad::types::ModelingCmd::CurveGetEndPoints { .. }
| kittycad::types::ModelingCmd::FaceIsPlanar { .. }
| kittycad::types::ModelingCmd::FaceGetPosition { .. }
| kittycad::types::ModelingCmd::FaceGetGradient { .. }
| kittycad::types::ModelingCmd::PlaneIntersectAndProject { .. }
| kittycad::types::ModelingCmd::ImportFiles { .. }
| kittycad::types::ModelingCmd::Mass { .. }
| kittycad::types::ModelingCmd::Volume { .. }
| kittycad::types::ModelingCmd::Density { .. }
| kittycad::types::ModelingCmd::SurfaceArea { .. }
| kittycad::types::ModelingCmd::CenterOfMass { .. }
| kittycad::types::ModelingCmd::GetSketchModePlane { .. }
| kittycad::types::ModelingCmd::EntityGetDistance { .. }
| kittycad::types::ModelingCmd::EntityLinearPattern { .. }
| kittycad::types::ModelingCmd::EntityCircularPattern { .. }
| kittycad::types::ModelingCmd::Solid3DGetExtrusionFaceInfo { .. }) = cmd
else {
return false;
};
true
}
#[async_trait::async_trait]
impl EngineManager for EngineConnection {
async fn send_modeling_cmd(
&self,
flush_batch: bool,
id: uuid::Uuid,
source_range: crate::executor::SourceRange,
cmd: kittycad::types::ModelingCmd,
) -> Result<OkWebSocketResponseData, KclError> {
let req = WebSocketRequest::ModelingCmdReq {
cmd: cmd.clone(),
cmd_id: id,
};
println!("req {:?}", req);
if !flush_batch {
self.batch.lock().unwrap().push(req);
}
// If the batch only has this one command that expects a return value,
// fire it right away, or if we want to flush batch queue.
let is_sending = (is_cmd_with_return_values(&cmd) && self.batch.lock().unwrap().len() == 1)
|| flush_batch
|| is_cmd_with_return_values(&cmd);
// Return a fake modeling_request empty response.
if !is_sending {
println!("fake {:?}", cmd);
return Ok(OkWebSocketResponseData::Modeling {
modeling_response: kittycad::types::OkModelingCmdResponse::Empty {}
});
}
let batched_requests =
WebSocketRequest::ModelingCmdBatchReq {
requests: self.batch.lock().unwrap().iter().fold(vec![], |mut acc, val| {
let WebSocketRequest::ModelingCmdReq { cmd, cmd_id } = val else { return acc; };
acc.push(kittycad::types::ModelingCmdReq {
cmd: cmd.clone(),
cmd_id: *cmd_id,
});
acc
}),
batch_id: uuid::Uuid::new_v4()
};
let final_req = if self.batch.lock().unwrap().len() == 1 {
self.batch.lock().unwrap().get(0).unwrap().clone()
} else {
batched_requests
};
// Throw away the old batch queue.
self.batch.lock().unwrap().clear();
println!("final req {:?}", final_req);
// We pop off the responses to cleanup our mappings.
let id_final = match final_req {
WebSocketRequest::ModelingCmdBatchReq { requests: _, batch_id } => batch_id,
WebSocketRequest::ModelingCmdReq { cmd: _, cmd_id } => cmd_id,
_ => panic!("should not be possible"),
};
let (tx, rx) = oneshot::channel();
// Send the request to the engine, via the actor.
self.engine_req_tx
.send(ToEngineReq {
req: final_req.clone(),
req: WebSocketRequest::ModelingCmdReq {
cmd: cmd.clone(),
cmd_id: id,
},
request_sent: tx,
})
.await
@ -310,7 +200,6 @@ impl EngineManager for EngineConnection {
})
})?;
// Wait for the response.
let current_time = std::time::Instant::now();
while current_time.elapsed().as_secs() < 60 {
@ -322,9 +211,8 @@ impl EngineManager for EngineConnection {
}));
}
}
if let Some((_, resp)) = self.responses.remove(&id_final) {
println!("RESP {:?}", resp);
// We pop off the responses to cleanup our mappings.
if let Some((_, resp)) = self.responses.remove(&id) {
return if let Some(data) = &resp.resp {
Ok(data.clone())
} else {
@ -337,7 +225,7 @@ impl EngineManager for EngineConnection {
}
Err(KclError::Engine(KclErrorDetails {
message: format!("Modeling command timed out `{}`", id_final),
message: format!("Modeling command timed out `{}`", id),
source_ranges: vec![source_range],
}))
}

View File

@ -19,7 +19,6 @@ impl EngineConnection {
impl crate::engine::EngineManager for EngineConnection {
async fn send_modeling_cmd(
&self,
_flush_batch: bool,
_id: uuid::Uuid,
_source_range: crate::executor::SourceRange,
_cmd: kittycad::types::ModelingCmd,

View File

@ -13,7 +13,6 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
/// Send a modeling command and wait for the response message.
async fn send_modeling_cmd(
&self,
flush_batch: bool,
id: uuid::Uuid,
source_range: crate::executor::SourceRange,
cmd: kittycad::types::ModelingCmd,

View File

@ -1008,7 +1008,6 @@ pub async fn execute(
// Before we even start executing the program, set the units.
ctx.engine
.send_modeling_cmd(
false,
uuid::Uuid::new_v4(),
SourceRange::default(),
kittycad::types::ModelingCmd::SetSceneUnits {
@ -1220,17 +1219,6 @@ pub async fn execute(
}
}
// Fire the batch since we've reached the end.
// ctx.engine
// .send_modeling_cmd(
// true,
// uuid::Uuid::new_v4(),
// SourceRange::default(),
// // This is ignored when flush_batch is true.
// kittycad::types::ModelingCmd::EditModeExit {},
// )
// .await?;
Ok(memory.clone())
}

View File

@ -206,7 +206,7 @@ impl Args {
id: uuid::Uuid,
cmd: kittycad::types::ModelingCmd,
) -> Result<OkWebSocketResponseData, KclError> {
self.ctx.engine.send_modeling_cmd(false, id, self.source_range, cmd).await
self.ctx.engine.send_modeling_cmd(id, self.source_range, cmd).await
}
fn make_user_val_from_json(&self, j: serde_json::Value) -> Result<MemoryItem, KclError> {

View File

@ -21,9 +21,9 @@ async fn execute_and_snapshot(code: &str, units: kittycad::types::UnitLength) ->
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
// Create the client.
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
let client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
// uncomment to use a local server
client.set_base_url("http://localhost:8080/");
//client.set_base_url("http://system76-pc:8080/");
let ws = client
.modeling()
@ -45,7 +45,6 @@ async fn execute_and_snapshot(code: &str, units: kittycad::types::UnitLength) ->
ctx.engine
.send_modeling_cmd(
false,
uuid::Uuid::new_v4(),
kcl_lib::executor::SourceRange::default(),
kittycad::types::ModelingCmd::DefaultCameraLookAt {
@ -61,7 +60,6 @@ async fn execute_and_snapshot(code: &str, units: kittycad::types::UnitLength) ->
let resp = ctx
.engine
.send_modeling_cmd(
false,
uuid::Uuid::new_v4(),
kcl_lib::executor::SourceRange::default(),
kittycad::types::ModelingCmd::TakeSnapshot {

View File

@ -49,7 +49,6 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
let plane_id = uuid::Uuid::new_v4();
ctx.engine
.send_modeling_cmd(
false,
plane_id,
SourceRange::default(),
ModelingCmd::MakePlane {
@ -68,7 +67,6 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
// You can however get path info without sketch mode.
ctx.engine
.send_modeling_cmd(
false,
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::SketchModeEnable {
@ -84,7 +82,6 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
// We can't get control points of an existing sketch without being in edit mode.
ctx.engine
.send_modeling_cmd(
false,
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::EditModeEnter { target: sketch_id },