Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dynamic cursor depending on mouse scene state #1995

Merged
merged 9 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions e2e/playwright/flow-tests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,7 @@ test('Sketch on face', async ({ page, context }) => {
await page.getByRole('button', { name: 'Extrude' }).click()

await expect(page.getByTestId('command-bar')).toBeVisible()
await page.waitForTimeout(100)

await page.keyboard.press('Enter')
await expect(page.getByText('Confirm Extrude')).toBeVisible()
Expand Down
4 changes: 2 additions & 2 deletions e2e/playwright/snapshot-tests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,14 +363,14 @@ test('extrude on each default plane should be stable', async ({
// clear code
await u.removeCurrentCode()
// add makeCode('XZ')
await u.openAndClearDebugPanel()
await page.locator('.cm-content').fill(makeCode(plane))
// wait for execution done
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()

await page.getByText('Code').click()
await page.waitForTimeout(80)
await page.waitForTimeout(150)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had the screenshots fail on me again 😞

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dang sorry, we should just revert to the original way of loading in storage state 😞

await expect(page).toHaveScreenshot({
maxDiffPixels: 100,
})
Expand Down
33 changes: 31 additions & 2 deletions src/clientSideScene/ClientSideSceneComp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ import { useModelingContext } from 'hooks/useModelingContext'
import { cameraMouseDragGuards } from 'lib/cameraControls'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { useStore } from 'useStore'
import { DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
import { ReactCameraProperties } from './CameraControls'
import { throttle } from 'lib/utils'
import { sceneInfra } from 'lib/singletons'
import {
EXTRA_SEGMENT_HANDLE,
PROFILE_START,
getParentGroup,
} from './sceneEntities'

function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
const [isCamMoving, setIsCamMoving] = useState(false)
Expand Down Expand Up @@ -40,7 +45,7 @@ export const ClientSideScene = ({
>['settings']['context']['modeling']['mouseControls']['current']
}) => {
const canvasRef = useRef<HTMLDivElement>(null)
const { state, send } = useModelingContext()
const { state, send, context } = useModelingContext()
const { hideClient, hideServer } = useShouldHideScene()
const { setHighlightRange } = useStore((s) => ({
setHighlightRange: s.setHighlightRange,
Expand Down Expand Up @@ -76,9 +81,33 @@ export const ClientSideScene = ({
}
}, [])

let cursor = 'default'
if (state.matches('Sketch')) {
if (
context.mouseState.type === 'isHovering' &&
getParentGroup(context.mouseState.on, [
ARROWHEAD,
EXTRA_SEGMENT_HANDLE,
PROFILE_START,
])
) {
cursor = 'move'
} else if (context.mouseState.type === 'isDragging') {
cursor = 'grabbing'
} else if (
state.matches('Sketch.Line tool') ||
state.matches('Sketch.Tangential arc to')
) {
cursor = 'crosshair'
} else {
cursor = 'default'
}
}

return (
<div
ref={canvasRef}
style={{ cursor: cursor }}
className={`absolute inset-0 h-full w-full transition-all duration-300 ${
hideClient ? 'opacity-0' : 'opacity-100'
} ${hideServer ? 'bg-black' : ''} ${
Expand Down
216 changes: 141 additions & 75 deletions src/clientSideScene/sceneEntities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ export const TANGENTIAL_ARC_TO__SEGMENT_DASH =
'tangential-arc-to-segment-body-dashed'
export const TANGENTIAL_ARC_TO_SEGMENT = 'tangential-arc-to-segment'
export const TANGENTIAL_ARC_TO_SEGMENT_BODY = 'tangential-arc-to-segment-body'
export const MIN_SEGMENT_LENGTH = 60 // in pixels
export const SEGMENT_WIDTH_PX = 1.6
export const HIDE_SEGMENT_LENGTH = 75 // in pixels
export const HIDE_HOVER_SEGMENT_LENGTH = 60 // in pixels

// This singleton Class is responsible for all of the things the user sees and interacts with.
// That mostly mean sketch elements.
Expand Down Expand Up @@ -563,7 +564,7 @@ export class SceneEntities {
},
})
},
...mouseEnterLeaveCallbacks(),
...this.mouseEnterLeaveCallbacks(),
})
}
setupSketchIdleCallbacks = ({
Expand Down Expand Up @@ -685,7 +686,7 @@ export class SceneEntities {
if (!event) return
sceneInfra.modelingSend(event)
},
...mouseEnterLeaveCallbacks(),
...this.mouseEnterLeaveCallbacks(),
})
}
prepareTruncatedMemoryAndAst = (
Expand Down Expand Up @@ -882,7 +883,16 @@ export class SceneEntities {
})

const pxLength = arcInfo.arcLength / scale
const shouldHide = pxLength < MIN_SEGMENT_LENGTH
const shouldHideIdle = pxLength < HIDE_SEGMENT_LENGTH
const shouldHideHover = pxLength < HIDE_HOVER_SEGMENT_LENGTH

const hoveredParent =
sceneInfra.hoveredObject &&
getParentGroup(sceneInfra.hoveredObject, [TANGENTIAL_ARC_TO_SEGMENT])
let isHandlesVisible = !shouldHideIdle
if (hoveredParent && hoveredParent?.uuid === group?.uuid) {
isHandlesVisible = !shouldHideHover
}

if (arrowGroup) {
arrowGroup.position.set(to[0], to[1], 0)
Expand All @@ -894,7 +904,7 @@ export class SceneEntities {
new Vector3(Math.cos(arrowheadAngle), Math.sin(arrowheadAngle), 0)
)
arrowGroup.scale.set(scale, scale, scale)
arrowGroup.visible = !shouldHide
arrowGroup.visible = isHandlesVisible
}

if (extraSegmentGroup) {
Expand All @@ -913,7 +923,7 @@ export class SceneEntities {
0
)
extraSegmentGroup.scale.set(scale, scale, scale)
extraSegmentGroup.visible = !shouldHide
extraSegmentGroup.visible = isHandlesVisible
}

const tangentialArcToSegmentBody = group.children.find(
Expand Down Expand Up @@ -970,7 +980,16 @@ export class SceneEntities {
)

const pxLength = length / scale
const shouldHide = pxLength < MIN_SEGMENT_LENGTH
const shouldHideIdle = pxLength < HIDE_SEGMENT_LENGTH
const shouldHideHover = pxLength < HIDE_HOVER_SEGMENT_LENGTH

const hoveredParent =
sceneInfra.hoveredObject &&
getParentGroup(sceneInfra.hoveredObject, [STRAIGHT_SEGMENT])
let isHandlesVisible = !shouldHideIdle
if (hoveredParent && hoveredParent?.uuid === group?.uuid) {
isHandlesVisible = !shouldHideHover
}

if (arrowGroup) {
arrowGroup.position.set(to[0], to[1], 0)
Expand All @@ -983,7 +1002,7 @@ export class SceneEntities {
.normalize()
arrowGroup.quaternion.setFromUnitVectors(new Vector3(0, 1, 0), dir)
arrowGroup.scale.set(scale, scale, scale)
arrowGroup.visible = !shouldHide
arrowGroup.visible = isHandlesVisible
}

const extraSegmentGroup = group.getObjectByName(EXTRA_SEGMENT_HANDLE)
Expand All @@ -997,7 +1016,7 @@ export class SceneEntities {
0
)
extraSegmentGroup.scale.set(scale, scale, scale)
extraSegmentGroup.visible = !shouldHide
extraSegmentGroup.visible = isHandlesVisible
}

const straightSegmentBody = group.children.find(
Expand Down Expand Up @@ -1175,6 +1194,119 @@ export class SceneEntities {
},
})
}
mouseEnterLeaveCallbacks() {
return {
onMouseEnter: ({ selected, dragSelected }: OnMouseEnterLeaveArgs) => {
if ([X_AXIS, Y_AXIS].includes(selected?.userData?.type)) {
const obj = selected as Mesh
const mat = obj.material as MeshBasicMaterial
mat.color.set(obj.userData.baseColor)
mat.color.offsetHSL(0, 0, 0.5)
}
const parent = getParentGroup(selected, [
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
PROFILE_START,
])
if (parent?.userData?.pathToNode) {
const updatedAst = parse(recast(kclManager.ast))
const node = getNodeFromPath<CallExpression>(
updatedAst,
parent.userData.pathToNode,
'CallExpression'
).node
sceneInfra.highlightCallback([node.start, node.end])
const yellow = 0xffff00
colorSegment(selected, yellow)
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
if (extraSegmentGroup) {
extraSegmentGroup.traverse((child) => {
if (child instanceof Points || child instanceof Mesh) {
child.material.opacity = dragSelected ? 0 : 1
}
})
}
const orthoFactor = orthoScale(sceneInfra.camControls.camera)

const factor =
(sceneInfra.camControls.camera instanceof OrthographicCamera
? orthoFactor
: perspScale(sceneInfra.camControls.camera, parent)) /
sceneInfra._baseUnitMultiplier
if (parent.name === STRAIGHT_SEGMENT) {
this.updateStraightSegment({
from: parent.userData.from,
to: parent.userData.to,
group: parent,
scale: factor,
})
} else if (parent.name === TANGENTIAL_ARC_TO_SEGMENT) {
this.updateTangentialArcToSegment({
prevSegment: parent.userData.prevSegment,
from: parent.userData.from,
to: parent.userData.to,
group: parent,
scale: factor,
})
}
Comment on lines +1231 to +1251
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this block is the new code after mouseEnterLeaveCallbacks was move into class method because it to access other class methods this. updateStraightSegment and updateTangentialArcToSegment

return
}
sceneInfra.highlightCallback([0, 0])
},
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
sceneInfra.highlightCallback([0, 0])
const parent = getParentGroup(selected, [
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
PROFILE_START,
])
if (parent) {
const orthoFactor = orthoScale(sceneInfra.camControls.camera)

const factor =
(sceneInfra.camControls.camera instanceof OrthographicCamera
? orthoFactor
: perspScale(sceneInfra.camControls.camera, parent)) /
sceneInfra._baseUnitMultiplier
if (parent.name === STRAIGHT_SEGMENT) {
this.updateStraightSegment({
from: parent.userData.from,
to: parent.userData.to,
group: parent,
scale: factor,
})
} else if (parent.name === TANGENTIAL_ARC_TO_SEGMENT) {
this.updateTangentialArcToSegment({
prevSegment: parent.userData.prevSegment,
from: parent.userData.from,
to: parent.userData.to,
group: parent,
scale: factor,
})
}
}
Comment on lines +1263 to +1287
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same story as above, this is a new block of code, rest of mouseEnterLeaveCallbacks is largely untoched.

const isSelected = parent?.userData?.isSelected
colorSegment(
selected,
isSelected ? 0x0000ff : parent?.userData?.baseColor || 0xffffff
)
const extraSegmentGroup = parent?.getObjectByName(EXTRA_SEGMENT_HANDLE)
if (extraSegmentGroup) {
extraSegmentGroup.traverse((child) => {
if (child instanceof Points || child instanceof Mesh) {
child.material.opacity = 0
}
})
}
if ([X_AXIS, Y_AXIS].includes(selected?.userData?.type)) {
const obj = selected as Mesh
const mat = obj.material as MeshBasicMaterial
mat.color.set(obj.userData.baseColor)
if (obj.userData.isSelected) mat.color.offsetHSL(0, 0, 0.2)
}
},
}
}
}

export type DefaultPlaneStr = 'XY' | 'XZ' | 'YZ' | '-XY' | '-XZ' | '-YZ'
Expand Down Expand Up @@ -1417,69 +1549,3 @@ function massageFormats(a: any): Vector3 {
? new Vector3(a[0], a[1], a[2])
: new Vector3(a.x, a.y, a.z)
}

function mouseEnterLeaveCallbacks() {
return {
onMouseEnter: ({ selected, dragSelected }: OnMouseEnterLeaveArgs) => {
if ([X_AXIS, Y_AXIS].includes(selected?.userData?.type)) {
const obj = selected as Mesh
const mat = obj.material as MeshBasicMaterial
mat.color.set(obj.userData.baseColor)
mat.color.offsetHSL(0, 0, 0.5)
}
const parent = getParentGroup(selected, [
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
PROFILE_START,
])
if (parent?.userData?.pathToNode) {
const updatedAst = parse(recast(kclManager.ast))
const node = getNodeFromPath<CallExpression>(
updatedAst,
parent.userData.pathToNode,
'CallExpression'
).node
sceneInfra.highlightCallback([node.start, node.end])
const yellow = 0xffff00
colorSegment(selected, yellow)
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
if (extraSegmentGroup) {
extraSegmentGroup.traverse((child) => {
if (child instanceof Points || child instanceof Mesh) {
child.material.opacity = dragSelected ? 0 : 1
}
})
}
return
}
sceneInfra.highlightCallback([0, 0])
},
onMouseLeave: ({ selected }: OnMouseEnterLeaveArgs) => {
sceneInfra.highlightCallback([0, 0])
const parent = getParentGroup(selected, [
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
PROFILE_START,
])
const isSelected = parent?.userData?.isSelected
colorSegment(
selected,
isSelected ? 0x0000ff : parent?.userData?.baseColor || 0xffffff
)
const extraSegmentGroup = parent?.getObjectByName(EXTRA_SEGMENT_HANDLE)
if (extraSegmentGroup) {
extraSegmentGroup.traverse((child) => {
if (child instanceof Points || child instanceof Mesh) {
child.material.opacity = 0
}
})
}
if ([X_AXIS, Y_AXIS].includes(selected?.userData?.type)) {
const obj = selected as Mesh
const mat = obj.material as MeshBasicMaterial
mat.color.set(obj.userData.baseColor)
if (obj.userData.isSelected) mat.color.offsetHSL(0, 0, 0.2)
}
},
}
}
Loading
Loading