[Feature]: Initialize engine with view_isometric
as a user. (#6604)
* feature: initialize system to view_isometric, playwright(e2e) does zoom_to_fit * fix: PR fixes * fix: removing testing code * fix: I definitely bricked my .spacemacs config and it is using the wrong eslint when I save files :( * fix: typo * fix: typoos * fix: fuking hack * chore: move exported var from e2e to src then reimport * fix: got em * fix: remove console log * fix: how did this get in that file?? moved it * fix: location for scene empty check zzz: * fix: removed debugging code * fix: forgot the hack for the load view without geometry workflow * fix: copy
This commit is contained in:
@ -3342,7 +3342,7 @@ profile001 = startProfile(sketch001, at = [-20, 20])
|
||||
const testPoint = { x: 590, y: 400 }
|
||||
const extrudeColor: [number, number, number] = [100, 100, 100]
|
||||
const sketchColor: [number, number, number] = [140, 140, 140]
|
||||
const defaultPlaneColor: [number, number, number] = [50, 50, 100]
|
||||
const defaultPlaneColor: [number, number, number] = [88, 44, 45]
|
||||
|
||||
const deleteOperation = async (operationButton: Locator) => {
|
||||
if (shouldUseKeyboard) {
|
||||
@ -3386,7 +3386,11 @@ profile001 = startProfile(sketch001, at = [-20, 20])
|
||||
)
|
||||
await deleteOperation(operationButton)
|
||||
await editor.expectEditor.toContain('')
|
||||
await scene.expectPixelColor(defaultPlaneColor, testPoint, 20)
|
||||
// Cannot use test point anymore because the camera's position has been
|
||||
// reset and the rest of the test doesn't need to change just to check
|
||||
// if the scene is cleared.
|
||||
// Check that the scene is cleared
|
||||
await scene.expectPixelColor(defaultPlaneColor, { x: 574, y: 342 }, 20)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -5,8 +5,6 @@ import { onboardingPaths } from '@src/routes/Onboarding/paths'
|
||||
|
||||
import type { Settings } from '@rust/kcl-lib/bindings/Settings'
|
||||
|
||||
export const IS_PLAYWRIGHT_KEY = 'playwright'
|
||||
|
||||
export const TEST_SETTINGS_KEY = '/settings.toml'
|
||||
export const TEST_SETTINGS: DeepPartial<Settings> = {
|
||||
app: {
|
||||
|
@ -18,11 +18,8 @@ import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfigu
|
||||
|
||||
import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist'
|
||||
import { secrets } from '@e2e/playwright/secrets'
|
||||
import {
|
||||
IS_PLAYWRIGHT_KEY,
|
||||
TEST_SETTINGS,
|
||||
TEST_SETTINGS_KEY,
|
||||
} from '@e2e/playwright/storageStates'
|
||||
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates'
|
||||
import { IS_PLAYWRIGHT_KEY } from '@src/lib/constants'
|
||||
import { test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
const toNormalizedCode = (text: string) => {
|
||||
|
@ -29,6 +29,13 @@ import { useSelector } from '@xstate/react'
|
||||
import type { MouseEventHandler } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useRouteLoaderData } from 'react-router-dom'
|
||||
import { isPlaywright } from '@src/lib/isPlaywright'
|
||||
import {
|
||||
engineStreamZoomToFit,
|
||||
engineViewIsometricWithGeometryPresent,
|
||||
engineViewIsometricWithoutGeometryPresent,
|
||||
} from '@src/lib/utils'
|
||||
import { DEFAULT_DEFAULT_LENGTH_UNIT } from '@src/lib/constants'
|
||||
|
||||
export const EngineStream = (props: {
|
||||
pool: string | null
|
||||
@ -89,21 +96,33 @@ export const EngineStream = (props: {
|
||||
console.log('scene is ready, fire!')
|
||||
|
||||
kmp
|
||||
.then(() =>
|
||||
// It makes sense to also call zoom to fit here, when a new file is
|
||||
// loaded for the first time, but not overtaking the work kevin did
|
||||
// so the camera isn't moving all the time.
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'zoom_to_fit',
|
||||
object_ids: [], // leave empty to zoom to all objects
|
||||
padding: 0.1, // padding around the objects
|
||||
animated: false, // don't animate the zoom for now
|
||||
},
|
||||
.then(async () => {
|
||||
// Gotcha: Playwright E2E tests will be zoom_to_fit, when you try to recreate the e2e test manually
|
||||
// your localhost will do view_isometric. Turn this boolean on to have the same experience when manually
|
||||
// debugging e2e tests
|
||||
|
||||
// We need a padding of 0.1 for zoom_to_fit for all E2E tests since they were originally
|
||||
// written with zoom_to_fit with padding 0.1
|
||||
const padding = 0.1
|
||||
if (isPlaywright()) {
|
||||
await engineStreamZoomToFit({ engineCommandManager, padding })
|
||||
} else {
|
||||
// If the scene is empty you cannot use view_isometric, it will not move the camera
|
||||
if (kclManager.isAstBodyEmpty(kclManager.ast)) {
|
||||
await engineViewIsometricWithoutGeometryPresent({
|
||||
engineCommandManager,
|
||||
unit:
|
||||
kclManager.fileSettings.defaultLengthUnit ||
|
||||
DEFAULT_DEFAULT_LENGTH_UNIT,
|
||||
})
|
||||
} else {
|
||||
await engineViewIsometricWithGeometryPresent({
|
||||
engineCommandManager,
|
||||
padding,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch(trap)
|
||||
}
|
||||
|
||||
|
@ -738,6 +738,18 @@ export class KclManager {
|
||||
return ast.start === 0 && ast.end === 0 && ast.body.length === 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if there is no code to execute. If there is a @settings annotation
|
||||
* that adds to the overall ast.start and ast.end but not the body which is the program
|
||||
*
|
||||
*
|
||||
* If you need to know if there is any program code or not, use this function otherwise
|
||||
* use _isAstEmpty
|
||||
*/
|
||||
isAstBodyEmpty(ast: Node<Program>) {
|
||||
return ast.body.length === 0
|
||||
}
|
||||
|
||||
get fileSettings() {
|
||||
return this._fileSettings
|
||||
}
|
||||
|
@ -187,3 +187,6 @@ export type ExecutionType =
|
||||
| typeof EXECUTION_TYPE_REAL
|
||||
| typeof EXECUTION_TYPE_MOCK
|
||||
| typeof EXECUTION_TYPE_NONE
|
||||
|
||||
/** Key for setting window.localStorage.setItem and .getItem to determine if the runtime is playwright for browsers */
|
||||
export const IS_PLAYWRIGHT_KEY = 'playwright'
|
||||
|
10
src/lib/isPlaywright.ts
Normal file
10
src/lib/isPlaywright.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { IS_PLAYWRIGHT_KEY } from '@src/lib/constants'
|
||||
|
||||
export function isPlaywright(): boolean {
|
||||
// ? checks for browser
|
||||
const electronRunTimePlaywright =
|
||||
window?.electron?.process?.env.IS_PLAYWRIGHT == 'true'
|
||||
const browserRuntimePlaywright =
|
||||
localStorage.getItem(IS_PLAYWRIGHT_KEY) === 'true'
|
||||
return electronRunTimePlaywright || browserRuntimePlaywright
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
import type { PlatformPath } from 'path'
|
||||
|
||||
import type { Configuration } from '@rust/kcl-lib/bindings/Configuration'
|
||||
|
||||
import { IS_PLAYWRIGHT_KEY } from '@e2e/playwright/storageStates'
|
||||
import { IS_PLAYWRIGHT_KEY } from '@src/lib/constants'
|
||||
|
||||
import {
|
||||
BROWSER_FILE_NAME,
|
||||
|
137
src/lib/utils.ts
137
src/lib/utils.ts
@ -6,6 +6,14 @@ import type { CallExpressionKw, SourceRange } from '@src/lang/wasm'
|
||||
import { isDesktop } from '@src/lib/isDesktop'
|
||||
import type { AsyncFn } from '@src/lib/types'
|
||||
|
||||
import * as THREE from 'three'
|
||||
|
||||
import type { EngineCommandManager } from '@src/lang/std/engineConnection'
|
||||
import type {
|
||||
CameraViewState_type,
|
||||
UnitLength_type,
|
||||
} from '@kittycad/lib/dist/types/src/models'
|
||||
|
||||
export const uuidv4 = v4
|
||||
|
||||
/**
|
||||
@ -533,3 +541,132 @@ export function getInVariableCase(name: string, prefixIfDigit = 'm') {
|
||||
|
||||
return likelyPascalCase.slice(0, 1).toLowerCase() + likelyPascalCase.slice(1)
|
||||
}
|
||||
|
||||
export function computeIsometricQuaternionForEmptyScene() {
|
||||
// Create the direction vector you want to look from
|
||||
const isoDir = new THREE.Vector3(1, 1, 1).normalize() // isometric look direction
|
||||
|
||||
// Target is the point you want to look at (e.g., origin)
|
||||
const target = new THREE.Vector3(0, 0, 0)
|
||||
|
||||
// Compute quaternion for isometric view
|
||||
const up = new THREE.Vector3(0, 0, 1) // default up direction
|
||||
const quaternion = new THREE.Quaternion()
|
||||
quaternion.setFromUnitVectors(new THREE.Vector3(0, 0, 1), isoDir) // align -Z with isoDir
|
||||
|
||||
// Align up vector using a lookAt matrix
|
||||
const m = new THREE.Matrix4()
|
||||
m.lookAt(new THREE.Vector3().addVectors(target, isoDir), target, up)
|
||||
quaternion.setFromRotationMatrix(m)
|
||||
return quaternion
|
||||
}
|
||||
|
||||
export async function engineStreamZoomToFit({
|
||||
engineCommandManager,
|
||||
padding,
|
||||
}: {
|
||||
engineCommandManager: EngineCommandManager
|
||||
padding: number
|
||||
}) {
|
||||
// It makes sense to also call zoom to fit here, when a new file is
|
||||
// loaded for the first time, but not overtaking the work kevin did
|
||||
// so the camera isn't moving all the time.
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'zoom_to_fit',
|
||||
object_ids: [], // leave empty to zoom to all objects
|
||||
padding, // padding around the objects
|
||||
animated: false, // don't animate the zoom for now
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function engineViewIsometricWithGeometryPresent({
|
||||
engineCommandManager,
|
||||
padding,
|
||||
}: {
|
||||
engineCommandManager: EngineCommandManager
|
||||
padding: number
|
||||
}) {
|
||||
/**
|
||||
* Default all users to view_isometric when loading into the engine.
|
||||
* This works for perspective projection and orthographic projection
|
||||
* This does not change the projection of the camera only the view direction which makes
|
||||
* it safe to use with either projection defaulted
|
||||
*/
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'view_isometric',
|
||||
padding, // padding around the objects
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* HACK: We need to update the gizmo, the command above doesn't trigger gizmo
|
||||
* to render which makes the axis point in an old direction.
|
||||
*/
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_get_settings',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function engineViewIsometricWithoutGeometryPresent({
|
||||
engineCommandManager,
|
||||
unit,
|
||||
}: {
|
||||
engineCommandManager: EngineCommandManager
|
||||
unit?: UnitLength_type
|
||||
}) {
|
||||
// If you load an empty scene with any file unit it will have an eye offset of this
|
||||
const MAGIC_ENGINE_EYE_OFFSET = 1378.0057
|
||||
const quat = computeIsometricQuaternionForEmptyScene()
|
||||
const isometricView: CameraViewState_type = {
|
||||
pivot_rotation: {
|
||||
x: quat.x,
|
||||
y: quat.y,
|
||||
z: quat.z,
|
||||
w: quat.w,
|
||||
},
|
||||
pivot_position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
},
|
||||
eye_offset: MAGIC_ENGINE_EYE_OFFSET,
|
||||
fov_y: 45,
|
||||
ortho_scale_factor: 1.4063792,
|
||||
is_ortho: true,
|
||||
ortho_scale_enabled: true,
|
||||
world_coord_system: 'right_handed_up_z',
|
||||
}
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_set_view',
|
||||
view: {
|
||||
...isometricView,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* HACK: We need to update the gizmo, the command above doesn't trigger gizmo
|
||||
* to render which makes the axis point in an old direction.
|
||||
*/
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_get_settings',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -2435,7 +2435,9 @@ export const modelingMachine = setup({
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(extrudeNode)) {
|
||||
return new Error("Couldn't find extrude node", { cause: extrudeNode })
|
||||
return new Error("Couldn't find extrude node", {
|
||||
cause: extrudeNode,
|
||||
})
|
||||
}
|
||||
|
||||
// Perform the shell op
|
||||
@ -3326,11 +3328,7 @@ export const modelingMachine = setup({
|
||||
'Artifact graph emptied': 'hidePlanes',
|
||||
},
|
||||
|
||||
entry: [
|
||||
'show default planes',
|
||||
'reset camera position',
|
||||
'set selection filter to curves only',
|
||||
],
|
||||
entry: ['show default planes', 'set selection filter to curves only'],
|
||||
description: `We want to disable selections and hover highlights here, because users can't do anything with that information until they actually add something to the scene. The planes are just for orientation here.`,
|
||||
exit: 'set selection filter to defaults',
|
||||
},
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { NODE_ENV } from '@src/env'
|
||||
import { isDesktop } from '@src/lib/isDesktop'
|
||||
|
||||
import { IS_PLAYWRIGHT_KEY } from '@e2e/playwright/storageStates'
|
||||
import { IS_PLAYWRIGHT_KEY } from '@src/lib/constants'
|
||||
|
||||
const isTestEnv = window?.localStorage.getItem(IS_PLAYWRIGHT_KEY) === 'true'
|
||||
|
||||
|
Reference in New Issue
Block a user