Add a trackball camera setting (#4764)
* Add a setting that does nothing * Make the setting actually change the interaction type * fmt * Bump `@kittycad/lib` to get the proper camera drag interaction types * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * Fix camera orientation bugs to support proper camera resetting on "camera orbit" setting change (#5031) * Add a setting that does nothing * Make the setting actually change the interaction type * fmt * fix: up vector bug fix and camera reset fix. Pushing code to cleanup after debugging * fix: deleting debugging code * fix: removing debugging code * fix: removing debugging console log * fix: removing console log debugs * fix: adding comment, restoring code from debugging * fix: removed lookAt when the orientation is already set from the engine.. I do not think we should be recomputing it? * fix: this fixes the bug because I was pointing to the getter not the value * Remove unused imports * Fix lint for unawaited Promise * Remove pointless change --------- Co-authored-by: Frank Noirot <frank@kittycad.io> Co-authored-by: Frank Noirot <frankjohnson1993@gmail.com> * Re-run CI * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * Re-run CI * Add display attributes to try to fix cargo test * Remove backwards compat test case it's failing because I didn't add cameraOrbit to that type and I don't want to * Fix test value (prev user value would have been Spherical before Trackball) --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Kevin Nadro <nadr0@users.noreply.github.com> Co-authored-by: Pierre Jacquier <pierre@zoo.dev>
This commit is contained in:
@ -29,6 +29,7 @@ import * as TWEEN from '@tweenjs/tween.js'
|
||||
import { isQuaternionVertical } from './helpers'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
||||
import { CameraDragInteractionType_type } from '@kittycad/lib/dist/types/src/models'
|
||||
|
||||
const ORTHOGRAPHIC_CAMERA_SIZE = 20
|
||||
const FRAMES_TO_ANIMATE_IN = 30
|
||||
@ -406,7 +407,7 @@ export class CameraControls {
|
||||
.sub(this.mouseDownPosition)
|
||||
this.mouseDownPosition.copy(this.mouseNewPosition)
|
||||
|
||||
const interaction = this.getInteractionType(event)
|
||||
let interaction = this.getInteractionType(event)
|
||||
if (interaction === 'none') return
|
||||
|
||||
// If there's a valid interaction and the mouse is moving,
|
||||
@ -753,8 +754,6 @@ export class CameraControls {
|
||||
didChange = true
|
||||
}
|
||||
|
||||
this.safeLookAtTarget(this.camera.up)
|
||||
|
||||
// Update the camera's matrices
|
||||
this.camera.updateMatrixWorld()
|
||||
if (didChange || forceUpdate) {
|
||||
@ -1189,14 +1188,24 @@ export class CameraControls {
|
||||
this.deferReactUpdate(this.reactCameraProperties)
|
||||
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
||||
}
|
||||
getInteractionType = (event: MouseEvent) =>
|
||||
_getInteractionType(
|
||||
getInteractionType = (
|
||||
event: MouseEvent
|
||||
): CameraDragInteractionType_type | 'none' => {
|
||||
const initialInteractionType = _getInteractionType(
|
||||
this.interactionGuards,
|
||||
event,
|
||||
this.enablePan,
|
||||
this.enableRotate,
|
||||
this.enableZoom
|
||||
)
|
||||
if (
|
||||
initialInteractionType === 'rotate' &&
|
||||
this.engineCommandManager.settings.cameraOrbit === 'trackball'
|
||||
) {
|
||||
return 'rotatetrackball'
|
||||
}
|
||||
return initialInteractionType
|
||||
}
|
||||
}
|
||||
|
||||
// Pure function helpers
|
||||
|
@ -119,6 +119,7 @@ export const ModelingMachineProvider = ({
|
||||
cameraProjection,
|
||||
highlightEdges,
|
||||
showScaleGrid,
|
||||
cameraOrbit,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -1154,6 +1155,7 @@ export const ModelingMachineProvider = ({
|
||||
enableSSAO: enableSSAO.current,
|
||||
showScaleGrid: showScaleGrid.current,
|
||||
cameraProjection: cameraProjection.current,
|
||||
cameraOrbit: cameraOrbit.current,
|
||||
},
|
||||
token
|
||||
)
|
||||
@ -1183,6 +1185,13 @@ export const ModelingMachineProvider = ({
|
||||
editorManager.selectionRanges = modelingState.context.selectionRanges
|
||||
}, [modelingState.context.selectionRanges])
|
||||
|
||||
// When changing camera modes reset the camera to the default orientation to correct
|
||||
// the up vector otherwise the conconical orientation for the camera modes will be
|
||||
// wrong
|
||||
useEffect(() => {
|
||||
sceneInfra.camControls.resetCameraPosition().catch(reportRejection)
|
||||
}, [cameraOrbit.current])
|
||||
|
||||
useEffect(() => {
|
||||
const onConnectionStateChanged = ({ detail }: CustomEvent) => {
|
||||
// If we are in sketch mode we need to exit it.
|
||||
|
@ -16,14 +16,15 @@ export function useSetupEngineManager(
|
||||
streamRef: React.RefObject<HTMLDivElement>,
|
||||
modelingSend: ReturnType<typeof useModelingContext>['send'],
|
||||
modelingContext: ReturnType<typeof useModelingContext>['context'],
|
||||
settings = {
|
||||
settings: SettingsViaQueryString = {
|
||||
pool: null,
|
||||
theme: Themes.System,
|
||||
highlightEdges: true,
|
||||
enableSSAO: true,
|
||||
showScaleGrid: false,
|
||||
cameraProjection: 'perspective',
|
||||
} as SettingsViaQueryString,
|
||||
cameraOrbit: 'spherical',
|
||||
},
|
||||
token?: string
|
||||
) {
|
||||
const networkContext = useNetworkContext()
|
||||
|
@ -1389,6 +1389,7 @@ export class EngineCommandManager extends EventTarget {
|
||||
enableSSAO: true,
|
||||
showScaleGrid: false,
|
||||
cameraProjection: 'perspective',
|
||||
cameraOrbit: 'spherical',
|
||||
}
|
||||
}
|
||||
|
||||
@ -1437,6 +1438,7 @@ export class EngineCommandManager extends EventTarget {
|
||||
enableSSAO: true,
|
||||
showScaleGrid: false,
|
||||
cameraProjection: 'orthographic',
|
||||
cameraOrbit: 'spherical',
|
||||
},
|
||||
// When passed, use a completely separate connecting code path that simply
|
||||
// opens a websocket and this is a function that is called when connected.
|
||||
|
@ -20,6 +20,7 @@ import { toSync } from 'lib/utils'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
||||
import { OnboardingStatus } from 'wasm-lib/kcl/bindings/OnboardingStatus'
|
||||
import { CameraOrbitType } from 'wasm-lib/kcl/bindings/CameraOrbitType'
|
||||
|
||||
/**
|
||||
* A setting that can be set at the user or project level
|
||||
@ -380,6 +381,30 @@ export function createSettings() {
|
||||
})),
|
||||
},
|
||||
}),
|
||||
/**
|
||||
* What methodology to use for orbiting the camera
|
||||
*/
|
||||
cameraOrbit: new Setting<CameraOrbitType>({
|
||||
defaultValue: 'spherical',
|
||||
hideOnLevel: 'project',
|
||||
description: 'What methodology to use for orbiting the camera',
|
||||
validate: (v) => ['spherical', 'trackball'].includes(v),
|
||||
commandConfig: {
|
||||
inputType: 'options',
|
||||
defaultValueFromContext: (context) =>
|
||||
context.modeling.cameraOrbit.current,
|
||||
options: (cmdContext, settingsContext) =>
|
||||
(['spherical', 'trackball'] as const).map((v) => ({
|
||||
name: v.charAt(0).toUpperCase() + v.slice(1),
|
||||
value: v,
|
||||
isCurrent:
|
||||
settingsContext.modeling.cameraOrbit.shouldShowCurrentLabel(
|
||||
cmdContext.argumentsToSubmit.level as SettingsLevel,
|
||||
v
|
||||
),
|
||||
})),
|
||||
},
|
||||
}),
|
||||
/**
|
||||
* Whether to highlight edges of 3D objects
|
||||
*/
|
||||
|
@ -4,6 +4,7 @@ import { AtLeast, PathValue, Paths } from 'lib/types'
|
||||
import { CommandArgumentConfig } from 'lib/commandTypes'
|
||||
import { Themes } from 'lib/theme'
|
||||
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
||||
import { CameraOrbitType } from 'wasm-lib/kcl/bindings/CameraOrbitType'
|
||||
|
||||
export interface SettingsViaQueryString {
|
||||
pool: string | null
|
||||
@ -12,6 +13,7 @@ export interface SettingsViaQueryString {
|
||||
enableSSAO: boolean
|
||||
showScaleGrid: boolean
|
||||
cameraProjection: CameraProjectionType
|
||||
cameraOrbit: CameraOrbitType
|
||||
}
|
||||
|
||||
export enum UnitSystem {
|
||||
|
@ -49,6 +49,7 @@ export function configurationToSettingsPayload(
|
||||
modeling: {
|
||||
defaultUnit: configuration?.settings?.modeling?.base_unit,
|
||||
cameraProjection: configuration?.settings?.modeling?.camera_projection,
|
||||
cameraOrbit: configuration?.settings?.modeling?.camera_orbit,
|
||||
mouseControls: mouseControlsToCameraSystem(
|
||||
configuration?.settings?.modeling?.mouse_controls
|
||||
),
|
||||
|
@ -259,6 +259,9 @@ pub struct ModelingSettings {
|
||||
/// The projection mode the camera should use while modeling.
|
||||
#[serde(default, alias = "cameraProjection", skip_serializing_if = "is_default")]
|
||||
pub camera_projection: CameraProjectionType,
|
||||
/// The methodology the camera should use to orbit around the model.
|
||||
#[serde(default, alias = "cameraOrbit", skip_serializing_if = "is_default")]
|
||||
pub camera_orbit: CameraOrbitType,
|
||||
/// The controls for how to navigate the 3D view.
|
||||
#[serde(default, alias = "mouseControls", skip_serializing_if = "is_default")]
|
||||
pub mouse_controls: MouseControlType,
|
||||
@ -415,6 +418,21 @@ pub enum CameraProjectionType {
|
||||
Orthographic,
|
||||
}
|
||||
|
||||
/// The types of camera orbit methods.
|
||||
#[derive(Debug, Default, Eq, PartialEq, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, Display, FromStr)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[display(style = "snake_case")]
|
||||
pub enum CameraOrbitType {
|
||||
/// Orbit using a spherical camera movement.
|
||||
#[default]
|
||||
#[display("spherical")]
|
||||
Spherical,
|
||||
/// Orbit using a trackball camera movement.
|
||||
#[display("trackball")]
|
||||
Trackball,
|
||||
}
|
||||
|
||||
/// Settings that affect the behavior of the KCL text editor.
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq, Eq, Validate)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@ -543,6 +561,8 @@ mod tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
use validator::Validate;
|
||||
|
||||
use crate::settings::types::CameraOrbitType;
|
||||
|
||||
use super::{
|
||||
AppColor, AppSettings, AppTheme, AppearanceSettings, CameraProjectionType, CommandBarSettings, Configuration,
|
||||
ModelingSettings, OnboardingStatus, ProjectSettings, Settings, TextEditorSettings, UnitLength,
|
||||
@ -594,6 +614,7 @@ textWrapping = true
|
||||
modeling: ModelingSettings {
|
||||
base_unit: UnitLength::In,
|
||||
camera_projection: CameraProjectionType::Orthographic,
|
||||
camera_orbit: Default::default(),
|
||||
mouse_controls: Default::default(),
|
||||
highlight_edges: Default::default(),
|
||||
show_debug_panel: true,
|
||||
@ -656,6 +677,7 @@ includeSettings = false
|
||||
modeling: ModelingSettings {
|
||||
base_unit: UnitLength::Yd,
|
||||
camera_projection: Default::default(),
|
||||
camera_orbit: Default::default(),
|
||||
mouse_controls: Default::default(),
|
||||
highlight_edges: Default::default(),
|
||||
show_debug_panel: true,
|
||||
@ -723,6 +745,7 @@ defaultProjectName = "projects-$nnn"
|
||||
modeling: ModelingSettings {
|
||||
base_unit: UnitLength::Yd,
|
||||
camera_projection: Default::default(),
|
||||
camera_orbit: CameraOrbitType::Spherical,
|
||||
mouse_controls: Default::default(),
|
||||
highlight_edges: Default::default(),
|
||||
show_debug_panel: true,
|
||||
@ -802,6 +825,7 @@ projectDirectory = "/Users/macinatormax/Documents/kittycad-modeling-projects""#;
|
||||
modeling: ModelingSettings {
|
||||
base_unit: UnitLength::Mm,
|
||||
camera_projection: Default::default(),
|
||||
camera_orbit: Default::default(),
|
||||
mouse_controls: Default::default(),
|
||||
highlight_edges: true.into(),
|
||||
show_debug_panel: false,
|
||||
|
@ -129,6 +129,7 @@ includeSettings = false
|
||||
modeling: ModelingSettings {
|
||||
base_unit: UnitLength::Yd,
|
||||
camera_projection: Default::default(),
|
||||
camera_orbit: Default::default(),
|
||||
mouse_controls: Default::default(),
|
||||
highlight_edges: Default::default(),
|
||||
show_debug_panel: true,
|
||||
|
Reference in New Issue
Block a user