diff --git a/src/clientSideScene/CameraControls.ts b/src/clientSideScene/CameraControls.ts index 0294bf77a..cfbf09feb 100644 --- a/src/clientSideScene/CameraControls.ts +++ b/src/clientSideScene/CameraControls.ts @@ -108,6 +108,8 @@ export class CameraControls { interactionGuards: MouseGuard = cameraMouseDragGuards.Zoo isFovAnimationInProgress = false perspectiveFovBeforeOrtho = 45 + // NOTE: Duplicated state across Provider and singleton. Mapped from settingsMachine + _setting_allowOrbitInSketchMode = false get isPerspective() { return this.camera instanceof PerspectiveCamera } diff --git a/src/components/ModelingMachineProvider.tsx b/src/components/ModelingMachineProvider.tsx index 6d9a78529..09f8480ee 100644 --- a/src/components/ModelingMachineProvider.tsx +++ b/src/components/ModelingMachineProvider.tsx @@ -111,7 +111,7 @@ export const ModelingMachineProvider = ({ auth, settings: { context: { - app: { theme, enableSSAO }, + app: { theme, enableSSAO, allowOrbitInSketchMode }, modeling: { defaultUnit, cameraProjection, @@ -121,6 +121,7 @@ export const ModelingMachineProvider = ({ }, }, } = useSettingsAuthContext() + const previousAllowOrbitInSketchMode = useRef(allowOrbitInSketchMode.current) const navigate = useNavigate() const { context, send: fileMachineSend } = useFileContext() const { file } = useLoaderData() as IndexLoaderData @@ -634,7 +635,8 @@ export const ModelingMachineProvider = ({ input.plane ) await kclManager.updateAst(modifiedAst, false) - sceneInfra.camControls.enableRotate = false + sceneInfra.camControls.enableRotate = + sceneInfra.camControls._setting_allowOrbitInSketchMode sceneInfra.camControls.syncDirection = 'clientToEngine' await letEngineAnimateAndSyncCamAfter( @@ -647,6 +649,7 @@ export const ModelingMachineProvider = ({ zAxis: input.zAxis, yAxis: input.yAxis, origin: [0, 0, 0], + animateTargetId: input.planeId, } }), 'animate-to-sketch': fromPromise( @@ -671,6 +674,7 @@ export const ModelingMachineProvider = ({ origin: info.sketchDetails.origin.map( (a) => a / sceneInfra._baseUnitMultiplier ) as [number, number, number], + animateTargetId: info?.sketchDetails?.faceId || '', } } ), @@ -1188,6 +1192,41 @@ export const ModelingMachineProvider = ({ } }, [engineCommandManager.engineConnection, modelingSend]) + useEffect(() => { + // Only trigger this if the state actually changes, if it stays the same do not reload the camera + if ( + previousAllowOrbitInSketchMode.current === allowOrbitInSketchMode.current + ) { + //no op + previousAllowOrbitInSketchMode.current = allowOrbitInSketchMode.current + return + } + const inSketchMode = modelingState.matches('Sketch') + + // If you are in sketch mode and you disable the orbit, return back to the normal view to the target + if (!allowOrbitInSketchMode.current) { + const targetId = modelingState.context.sketchDetails?.animateTargetId + if (inSketchMode && targetId) { + letEngineAnimateAndSyncCamAfter(engineCommandManager, targetId) + .then(() => {}) + .catch((e) => { + console.error( + 'failed to sync engine and client scene after disabling allow orbit in sketch mode' + ) + console.error(e) + }) + } + } + + // While you are in sketch mode you should be able to control the enable rotate + // Once you exit it goes back to normal + if (inSketchMode) { + sceneInfra.camControls.enableRotate = allowOrbitInSketchMode.current + } + + previousAllowOrbitInSketchMode.current = allowOrbitInSketchMode.current + }, [allowOrbitInSketchMode]) + // Allow using the delete key to delete solids useHotkeys(['backspace', 'delete', 'del'], () => { modelingSend({ type: 'Delete selection' }) diff --git a/src/components/SettingsAuthProvider.tsx b/src/components/SettingsAuthProvider.tsx index 1198cd803..1aa8b858a 100644 --- a/src/components/SettingsAuthProvider.tsx +++ b/src/components/SettingsAuthProvider.tsx @@ -137,6 +137,11 @@ export const SettingsAuthProviderBase = ({ sceneInfra.theme = opposingTheme sceneEntitiesManager.updateSegmentBaseColor(opposingTheme) }, + setAllowOrbitInSketchMode: ({ context }) => { + sceneInfra.camControls._setting_allowOrbitInSketchMode = + context.app.allowOrbitInSketchMode.current + // ModelingMachineProvider will do a use effect to trigger the camera engine sync + }, toastSuccess: ({ event }) => { if (!('data' in event)) return const eventParts = event.type.replace(/^set./, '').split('.') as [ diff --git a/src/lib/settings/initialSettings.tsx b/src/lib/settings/initialSettings.tsx index 61b512e22..d2cd06df9 100644 --- a/src/lib/settings/initialSettings.tsx +++ b/src/lib/settings/initialSettings.tsx @@ -190,6 +190,14 @@ export function createSettings() { inputType: 'boolean', }, }), + allowOrbitInSketchMode: new Setting({ + defaultValue: false, + description: 'Toggle free camera while in sketch mode', + validate: (v) => typeof v === 'boolean', + commandConfig: { + inputType: 'boolean', + }, + }), onboardingStatus: new Setting({ defaultValue: '', // TODO: this could be better but we don't have a TS side real enum diff --git a/src/lib/settings/settingsUtils.ts b/src/lib/settings/settingsUtils.ts index 36c9bef22..bb1c7fe5c 100644 --- a/src/lib/settings/settingsUtils.ts +++ b/src/lib/settings/settingsUtils.ts @@ -41,6 +41,8 @@ export function configurationToSettingsPayload( onboardingStatus: configuration?.settings?.app?.onboarding_status, dismissWebBanner: configuration?.settings?.app?.dismiss_web_banner, streamIdleMode: configuration?.settings?.app?.stream_idle_mode, + allowOrbitInSketchMode: + configuration?.settings?.app?.allow_orbit_in_sketch_mode, projectDirectory: configuration?.settings?.project?.directory, enableSSAO: configuration?.settings?.modeling?.enable_ssao, }, @@ -80,6 +82,8 @@ export function projectConfigurationToSettingsPayload( onboardingStatus: configuration?.settings?.app?.onboarding_status, dismissWebBanner: configuration?.settings?.app?.dismiss_web_banner, streamIdleMode: configuration?.settings?.app?.stream_idle_mode, + allowOrbitInSketchMode: + configuration?.settings?.app?.allow_orbit_in_sketch_mode, enableSSAO: configuration?.settings?.modeling?.enable_ssao, }, modeling: { diff --git a/src/machines/modelingMachine.ts b/src/machines/modelingMachine.ts index 3d0115dce..a3e4426a6 100644 --- a/src/machines/modelingMachine.ts +++ b/src/machines/modelingMachine.ts @@ -133,6 +133,8 @@ export interface SketchDetails { zAxis: [number, number, number] yAxis: [number, number, number] origin: [number, number, number] + // face id or plane id, both are strings + animateTargetId?: string } export interface SegmentOverlay { diff --git a/src/machines/settingsMachine.ts b/src/machines/settingsMachine.ts index 1c432c923..690fba1e0 100644 --- a/src/machines/settingsMachine.ts +++ b/src/machines/settingsMachine.ts @@ -43,6 +43,7 @@ export const settingsMachine = setup({ 'Execute AST': () => {}, toastSuccess: () => {}, setClientSideSceneUnits: () => {}, + setAllowOrbitInSketchMode: () => {}, persistSettings: () => {}, resetSettings: assign(({ context, event }) => { if (!('level' in event)) return {} @@ -157,6 +158,15 @@ export const settingsMachine = setup({ actions: ['setSettingAtLevel', 'toastSuccess'], }, + 'set.app.allowOrbitInSketchMode': { + target: 'persisting settings', + actions: [ + 'setSettingAtLevel', + 'toastSuccess', + 'setAllowOrbitInSketchMode', + ], + }, + 'set.modeling.cameraProjection': { target: 'persisting settings', @@ -183,6 +193,7 @@ export const settingsMachine = setup({ 'setClientSideSceneUnits', 'Execute AST', 'setClientTheme', + 'setAllowOrbitInSketchMode', ], }, @@ -194,6 +205,7 @@ export const settingsMachine = setup({ 'setClientSideSceneUnits', 'Execute AST', 'setClientTheme', + 'setAllowOrbitInSketchMode', ], }, diff --git a/src/wasm-lib/kcl/src/settings/types/mod.rs b/src/wasm-lib/kcl/src/settings/types/mod.rs index cc04f7fa8..9c6af29b4 100644 --- a/src/wasm-lib/kcl/src/settings/types/mod.rs +++ b/src/wasm-lib/kcl/src/settings/types/mod.rs @@ -121,6 +121,9 @@ pub struct AppSettings { /// When the user is idle, and this is true, the stream will be torn down. #[serde(default, alias = "streamIdleMode", skip_serializing_if = "is_default")] stream_idle_mode: bool, + /// When the user is idle, and this is true, the stream will be torn down. + #[serde(default, alias = "allowOrbitInSketchMode", skip_serializing_if = "is_default")] + allow_orbit_in_sketch_mode: bool, } // TODO: When we remove backwards compatibility with the old settings file, we can remove this. @@ -586,6 +589,7 @@ textWrapping = true dismiss_web_banner: false, enable_ssao: None, stream_idle_mode: false, + allow_orbit_in_sketch_mode: false, }, modeling: ModelingSettings { base_unit: UnitLength::In, @@ -647,6 +651,7 @@ includeSettings = false dismiss_web_banner: false, enable_ssao: None, stream_idle_mode: false, + allow_orbit_in_sketch_mode: false, }, modeling: ModelingSettings { base_unit: UnitLength::Yd, @@ -713,6 +718,7 @@ defaultProjectName = "projects-$nnn" dismiss_web_banner: false, enable_ssao: None, stream_idle_mode: false, + allow_orbit_in_sketch_mode: false, }, modeling: ModelingSettings { base_unit: UnitLength::Yd, @@ -791,6 +797,7 @@ projectDirectory = "/Users/macinatormax/Documents/kittycad-modeling-projects""#; dismiss_web_banner: false, enable_ssao: None, stream_idle_mode: false, + allow_orbit_in_sketch_mode: false, }, modeling: ModelingSettings { base_unit: UnitLength::Mm, diff --git a/src/wasm-lib/kcl/src/settings/types/project.rs b/src/wasm-lib/kcl/src/settings/types/project.rs index 314a3d7e0..eb28298ff 100644 --- a/src/wasm-lib/kcl/src/settings/types/project.rs +++ b/src/wasm-lib/kcl/src/settings/types/project.rs @@ -124,6 +124,7 @@ includeSettings = false dismiss_web_banner: false, enable_ssao: None, stream_idle_mode: false, + allow_orbit_in_sketch_mode: false, }, modeling: ModelingSettings { base_unit: UnitLength::Yd,