diff --git a/src/clientSideScene/CameraControls.ts b/src/clientSideScene/CameraControls.ts index c7282003c..c74f61d53 100644 --- a/src/clientSideScene/CameraControls.ts +++ b/src/clientSideScene/CameraControls.ts @@ -390,28 +390,7 @@ export class CameraControls { return } - // Implement camera movement logic here based on deltaMove - // For example, for rotating the camera around the target: - if (interaction === 'rotate') { - this.pendingRotation = this.pendingRotation - ? this.pendingRotation - : new Vector2() - this.pendingRotation.x += deltaMove.x - this.pendingRotation.y += deltaMove.y - } else if (interaction === 'zoom') { - this.pendingZoom = this.pendingZoom ? this.pendingZoom : 1 - this.pendingZoom *= 1 + deltaMove.y * 0.01 - } else if (interaction === 'pan') { - this.pendingPan = this.pendingPan ? this.pendingPan : new Vector2() - let distance = this.camera.position.distanceTo(this.target) - if (this.camera instanceof OrthographicCamera) { - const zoomFudgeFactor = 2280 - distance = zoomFudgeFactor / (this.camera.zoom * 45) - } - const panSpeed = (distance / 1000 / 45) * this.perspectiveFovBeforeOrtho - this.pendingPan.x += -deltaMove.x * panSpeed - this.pendingPan.y += deltaMove.y * panSpeed - } + this.moveCamera(interaction, deltaMove) } else { /** * If we're not in sketch mode and not dragging, we can highlight entities @@ -433,6 +412,31 @@ export class CameraControls { } } + moveCamera(interaction: interactionType, deltaMove: Vector2) { + // Implement camera movement logic here based on deltaMove + // For example, for rotating the camera around the target: + if (interaction === 'rotate') { + this.pendingRotation = this.pendingRotation + ? this.pendingRotation + : new Vector2() + this.pendingRotation.x += deltaMove.x + this.pendingRotation.y += deltaMove.y + } else if (interaction === 'zoom') { + this.pendingZoom = this.pendingZoom ? this.pendingZoom : 1 + this.pendingZoom *= 1 + deltaMove.y * 0.01 + } else if (interaction === 'pan') { + this.pendingPan = this.pendingPan ? this.pendingPan : new Vector2() + let distance = this.camera.position.distanceTo(this.target) + if (this.camera instanceof OrthographicCamera) { + const zoomFudgeFactor = 2280 + distance = zoomFudgeFactor / (this.camera.zoom * 45) + } + const panSpeed = (distance / 1000 / 45) * this.perspectiveFovBeforeOrtho + this.pendingPan.x += -deltaMove.x * panSpeed + this.pendingPan.y += deltaMove.y * panSpeed + } + } + onMouseUp = (event: PointerEvent) => { this.domElement.releasePointerCapture(event.pointerId) this.isDragging = false @@ -457,14 +461,56 @@ export class CameraControls { if (interaction === 'none') return event.preventDefault() + const zoomDirection = this.interactionGuards.zoom.scrollReverseY ? -1 : 1 if (this.syncDirection === 'engineToClient') { if (interaction === 'zoom') { - this.zoomDataFromLastFrame = event.deltaY + this.zoomDataFromLastFrame = event.deltaY * zoomDirection } else { - // This case will get handled when we add pan and rotate using Apple trackpad. - console.error( - `Unexpected interaction type for engineToClient wheel event: ${interaction}` - ) + this.isDragging = true + this.handleStart() + + this.engineCommandManager + .sendSceneCommand({ + type: 'modeling_cmd_batch_req', + batch_id: uuidv4(), + requests: [ + { + cmd: { + type: 'camera_drag_start', + interaction, + window: { x: event.clientX, y: event.clientY }, + }, + cmd_id: uuidv4(), + }, + { + cmd: { + type: 'camera_drag_move', + interaction, + window: { + x: event.clientX - event.deltaX, + y: event.clientY - event.deltaY, + }, + }, + cmd_id: uuidv4(), + }, + { + cmd: { + type: 'camera_drag_end', + interaction, + window: { + x: event.clientX - event.deltaX, + y: event.clientY - event.deltaY, + }, + }, + cmd_id: uuidv4(), + }, + ], + responses: false, + }) + .catch(reportRejection) + + this.isDragging = false + this.handleEnd() } return } @@ -478,12 +524,19 @@ export class CameraControls { this.handleStart() if (interaction === 'zoom') { - this.pendingZoom = 1 + (event.deltaY / window.devicePixelRatio) * 0.001 + this.pendingZoom = + 1 + (event.deltaY / window.devicePixelRatio) * 0.001 * zoomDirection } else { - // This case will get handled when we add pan and rotate using Apple trackpad. - console.error( - `Unexpected interaction type for wheel event: ${interaction}` + this.isDragging = true + this.mouseDownPosition.set(event.clientX, event.clientY) + + this.moveCamera(interaction, new Vector2(-event.deltaX, -event.deltaY)) + + this.mouseDownPosition.set( + event.clientX + event.deltaX, + event.clientY + event.deltaY ) + this.isDragging = false } this.handleEnd() } @@ -1266,6 +1319,9 @@ function _getInteractionType( enableZoom: boolean ): interactionType | 'none' { if (event instanceof WheelEvent) { + if (enablePan && interactionGuards.pan.scrollCallback(event)) return 'pan' + if (enableRotate && interactionGuards.rotate.scrollCallback(event)) + return 'rotate' if (enableZoom && interactionGuards.zoom.scrollCallback(event)) return 'zoom' } else { diff --git a/src/lib/cameraControls.ts b/src/lib/cameraControls.ts index 07fac6435..1d7540570 100644 --- a/src/lib/cameraControls.ts +++ b/src/lib/cameraControls.ts @@ -13,6 +13,7 @@ export type CameraSystem = | 'KittyCAD' | 'OnShape' | 'Trackpad Friendly' + | 'Apple Trackpad' | 'Solidworks' | 'NX' | 'Creo' @@ -22,6 +23,7 @@ export const cameraSystems: CameraSystem[] = [ 'KittyCAD', 'OnShape', 'Trackpad Friendly', + 'Apple Trackpad', 'Solidworks', 'NX', 'Creo', @@ -38,6 +40,8 @@ export function mouseControlsToCameraSystem( return 'OnShape' case 'trackpad_friendly': return 'Trackpad Friendly' + case 'apple_trackpad': + return 'Apple Trackpad' case 'solidworks': return 'Solidworks' case 'nx': @@ -54,6 +58,7 @@ export function mouseControlsToCameraSystem( interface MouseGuardHandler { description: string callback: (e: MouseEvent) => boolean + scrollCallback: (e: WheelEvent) => boolean lenientDragStartButton?: number } @@ -61,6 +66,7 @@ interface MouseGuardZoomHandler { description: string dragCallback: (e: MouseEvent) => boolean scrollCallback: (e: WheelEvent) => boolean + scrollReverseY?: boolean lenientDragStartButton?: number } @@ -83,6 +89,7 @@ export const cameraMouseDragGuards: Record = { callback: (e) => (btnName(e).middle && noModifiersPressed(e)) || (btnName(e).right && e.shiftKey), + scrollCallback: () => false, }, zoom: { description: 'Scroll or Ctrl + Right click drag', @@ -92,6 +99,7 @@ export const cameraMouseDragGuards: Record = { rotate: { description: 'Right click drag', callback: (e) => btnName(e).right && noModifiersPressed(e), + scrollCallback: () => false, }, }, OnShape: { @@ -100,6 +108,7 @@ export const cameraMouseDragGuards: Record = { callback: (e) => (btnName(e).right && e.ctrlKey) || (btnName(e).middle && noModifiersPressed(e)), + scrollCallback: () => false, }, zoom: { description: 'Scroll', @@ -109,6 +118,7 @@ export const cameraMouseDragGuards: Record = { rotate: { description: 'Right click drag', callback: (e) => btnName(e).right && noModifiersPressed(e), + scrollCallback: () => false, }, }, 'Trackpad Friendly': { @@ -117,6 +127,7 @@ export const cameraMouseDragGuards: Record = { callback: (e) => (btnName(e).left && e.altKey && e.shiftKey && !e.metaKey) || (btnName(e).middle && noModifiersPressed(e)), + scrollCallback: () => false, }, zoom: { description: `Scroll or ${ALT} + ${META} + Left click drag`, @@ -126,13 +137,44 @@ export const cameraMouseDragGuards: Record = { rotate: { description: `${ALT} + Left click drag`, callback: (e) => btnName(e).left && e.altKey && !e.shiftKey && !e.metaKey, + scrollCallback: () => false, lenientDragStartButton: 0, }, }, + 'Apple Trackpad': { + pan: { + description: `Scroll or one finger drag`, + callback: (e) => btnName(e).left && noModifiersPressed(e), + scrollCallback: (e) => e.deltaMode === 0 && noModifiersPressed(e), + lenientDragStartButton: 0, + }, + zoom: { + description: `Shift + Scroll`, + dragCallback: (e) => false, + scrollCallback: (e) => + e.deltaMode === 0 && + e.shiftKey && + !e.ctrlKey && + !e.altKey && + !e.metaKey, + scrollReverseY: true, + }, + rotate: { + description: `${ALT} + Scroll`, + callback: (e) => false, + scrollCallback: (e) => + e.deltaMode === 0 && + e.altKey && + !e.ctrlKey && + !e.shiftKey && + !e.metaKey, + }, + }, Solidworks: { pan: { description: 'Ctrl + Right click drag', callback: (e) => btnName(e).right && e.ctrlKey, + scrollCallback: () => false, lenientDragStartButton: 2, }, zoom: { @@ -143,12 +185,14 @@ export const cameraMouseDragGuards: Record = { rotate: { description: 'Middle click drag', callback: (e) => btnName(e).middle && noModifiersPressed(e), + scrollCallback: () => false, }, }, NX: { pan: { description: 'Shift + Middle click drag', callback: (e) => btnName(e).middle && e.shiftKey, + scrollCallback: () => false, }, zoom: { description: 'Scroll or Ctrl + Middle click drag', @@ -158,12 +202,14 @@ export const cameraMouseDragGuards: Record = { rotate: { description: 'Middle click drag', callback: (e) => btnName(e).middle && noModifiersPressed(e), + scrollCallback: () => false, }, }, Creo: { pan: { description: 'Ctrl + Left click drag', callback: (e) => btnName(e).left && !btnName(e).right && e.ctrlKey, + scrollCallback: () => false, }, zoom: { description: 'Scroll or Ctrl + Right click drag', @@ -176,12 +222,14 @@ export const cameraMouseDragGuards: Record = { const b = btnName(e) return (b.middle || (b.left && b.right)) && e.ctrlKey }, + scrollCallback: () => false, }, }, AutoCAD: { pan: { description: 'Middle click drag', callback: (e) => btnName(e).middle && noModifiersPressed(e), + scrollCallback: () => false, }, zoom: { description: 'Scroll', @@ -191,6 +239,7 @@ export const cameraMouseDragGuards: Record = { rotate: { description: 'Shift + Middle click drag', callback: (e) => btnName(e).middle && e.shiftKey, + scrollCallback: () => false, }, }, } diff --git a/src/wasm-lib/kcl/src/settings/types/mod.rs b/src/wasm-lib/kcl/src/settings/types/mod.rs index 231a99836..c0579e4c9 100644 --- a/src/wasm-lib/kcl/src/settings/types/mod.rs +++ b/src/wasm-lib/kcl/src/settings/types/mod.rs @@ -389,6 +389,8 @@ pub enum MouseControlType { OnShape, #[serde(alias = "Trackpad Friendly")] TrackpadFriendly, + #[serde(alias = "Apple Trackpad")] + AppleTrackpad, #[serde(alias = "Solidworks")] Solidworks, #[serde(alias = "NX")]