Skip to content

Commit

Permalink
Fixed a ton of tooltips popping up in 3d viewer (#52)
Browse files Browse the repository at this point in the history
  • Loading branch information
Abse2001 authored Dec 4, 2024
1 parent b4e7ff5 commit ba4a203
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 74 deletions.
37 changes: 28 additions & 9 deletions src/CadViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ interface Props {
}

export const CadViewer = ({ soup, children }: Props) => {
const [hoveredComponent, setHoveredComponent] = useState<string | null>(null)
const [hoveredComponent, setHoveredComponent] = useState<{
id: string | null
name: string | null
mousePosition: [number, number, number] | null
}>({
id: null,
name: null,
mousePosition: null,
})
soup ??= useConvertChildrenToSoup(children, soup) as any

if (!soup) return null
Expand All @@ -33,9 +41,20 @@ export const CadViewer = ({ soup, children }: Props) => {

const cad_components = su(soup).cad_component.list()

// TODO canvas/camera etc.
const handleHover = (
componentId: string | null,
componentName?: string,
mousePosition?: [number, number, number],
) => {
setHoveredComponent({
id: componentId,
name: componentName || null,
mousePosition: mousePosition || null,
})
}

return (
<CadViewerContainer>
<CadViewerContainer hoveredComponent={hoveredComponent}>
{stls.map(({ stlUrl, color }, index) => (
<STLModel
key={stlUrl}
Expand Down Expand Up @@ -74,8 +93,8 @@ export const CadViewer = ({ soup, children }: Props) => {
rotation={rotationOffset}
componentId={cad_component.cad_component_id}
name={componentName || cad_component.cad_component_id}
onHover={setHoveredComponent}
isHovered={hoveredComponent === cad_component.cad_component_id}
onHover={handleHover}
isHovered={hoveredComponent.id === cad_component.cad_component_id}
/>
)
}
Expand All @@ -88,8 +107,8 @@ export const CadViewer = ({ soup, children }: Props) => {
rotationOffset={rotationOffset}
componentId={cad_component.cad_component_id}
name={componentName || cad_component.cad_component_id}
onHover={setHoveredComponent}
isHovered={hoveredComponent === cad_component.cad_component_id}
onHover={handleHover}
isHovered={hoveredComponent.id === cad_component.cad_component_id}
/>
)
}
Expand All @@ -110,8 +129,8 @@ export const CadViewer = ({ soup, children }: Props) => {
footprint={cad_component.footprinter_string}
componentId={cad_component.cad_component_id}
name={componentName || cad_component.cad_component_id}
onHover={setHoveredComponent}
isHovered={hoveredComponent === cad_component.cad_component_id}
onHover={handleHover}
isHovered={hoveredComponent.id === cad_component.cad_component_id}
/>
)
}
Expand Down
34 changes: 28 additions & 6 deletions src/CadViewerContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,19 @@ export const RotationTracker = () => {
return <></>
}

export const CadViewerContainer = ({ children }: { children: any }) => {
import { Html } from "@react-three/drei"

export const CadViewerContainer = ({
children,
hoveredComponent,
}: {
children: any
hoveredComponent: {
id: string | null
name: string | null
mousePosition: [number, number, number] | null
}
}) => {
return (
<div style={{ position: "relative", width: "100%", height: "100%" }}>
<div
Expand All @@ -26,8 +38,6 @@ export const CadViewerContainer = ({ children }: { children: any }) => {
<Canvas
camera={{
up: [0, 0, 1],
// rotation: [-Math.PI / 2, 0, 0],
// lookAt: new THREE.Vector3(0, 0, 0),
position: [1, 1, 1],
}}
style={{ zIndex: 10 }}
Expand Down Expand Up @@ -55,6 +65,21 @@ export const CadViewerContainer = ({ children }: { children: any }) => {
sectionSize={10}
/>
{children}
{hoveredComponent.id && hoveredComponent.mousePosition && (
<Html
position={hoveredComponent.mousePosition}
style={{
fontFamily: "sans-serif",
transform: "translate3d(50%, 50%, 0)",
backgroundColor: "white",
padding: "5px",
borderRadius: "3px",
pointerEvents: "none",
}}
>
{hoveredComponent.name}
</Html>
)}
</Canvas>
<div
style={{
Expand All @@ -64,9 +89,6 @@ export const CadViewerContainer = ({ children }: { children: any }) => {
fontFamily: "sans-serif",
color: "white",
WebkitTextStroke: "0.5px rgba(0, 0, 0, 0.5)",
// color: "rgba(255, 255, 255, 0.75)",
// textShadow:
// "0px 0px 1px rgba(0, 0, 0, 0.5), 0px 0px 2px rgba(0, 0, 0, 0.5)",
fontSize: 11,
}}
>
Expand Down
105 changes: 46 additions & 59 deletions src/ContainerWithTooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Html } from "@react-three/drei"
import { GroupProps, useFrame, useThree } from "@react-three/fiber"
import { useRef, useState } from "react"
import { GroupProps, useThree } from "@react-three/fiber"
import { useRef, useCallback } from "react"
import type { Vector3 } from "three"
import * as THREE from "three"

Expand All @@ -16,73 +16,60 @@ const ContainerWithTooltip = ({
componentId: string
name: string
position?: Vector3 | [number, number, number]
onHover: (id: string | null) => void
onHover: (
id: string | null,
name?: string,
mousePosition?: [number, number, number],
) => void
isHovered: boolean
}) => {
const [mousePosition, setMousePosition] = useState<[number, number, number]>([
0, 0, 0,
])
const { camera } = useThree()
const mouseRef = useRef(new THREE.Vector2())
const lastValidPointRef = useRef<THREE.Vector3 | null>(null)

// Update tooltip position on every frame when hovered
useFrame(() => {
if (isHovered) {
// Project the stored mouse coordinates into 3D space
const vector = new THREE.Vector3(
mouseRef.current.x,
mouseRef.current.y,
0.5,
)
vector.unproject(camera)
setMousePosition([vector.x, vector.y, vector.z])
}
})

const groupProps: GroupProps = {
onPointerEnter: (e) => {
e.stopPropagation()
// Store normalized mouse coordinates
mouseRef.current.set(
(e.clientX / window.innerWidth) * 2 - 1,
-(e.clientY / window.innerHeight) * 2 + 1,
)
onHover(componentId)
},
onPointerMove: (e) => {
const handlePointerEnter = useCallback(
(e: any) => {
e.stopPropagation()
// Update normalized mouse coordinates
mouseRef.current.set(
(e.clientX / window.innerWidth) * 2 - 1,
-(e.clientY / window.innerHeight) * 2 + 1,
)

try {
// Fallback to event position if raycaster fails
const point =
e.point ||
(e.intersections && e.intersections.length > 0
? e.intersections[0].point
: null) ||
(position
? new THREE.Vector3(...(position as [number, number, number]))
: null)

if (point) {
lastValidPointRef.current = point
onHover(componentId, name, [point.x, point.y, point.z])
} else {
onHover(componentId, name)
}
} catch (error) {
console.warn("Hover event error:", error)
onHover(componentId, name)
}
},
onPointerLeave: (e) => {
[componentId, name, onHover, position],
)

const handlePointerLeave = useCallback(
(e: any) => {
e.stopPropagation()
lastValidPointRef.current = null
onHover(null)
},
[onHover],
)

const groupProps: GroupProps = {
onPointerEnter: handlePointerEnter,
onPointerMove: handlePointerEnter,
onPointerLeave: handlePointerLeave,
}

return (
<group {...groupProps}>
{children}
{isHovered && (
<Html
position={mousePosition}
style={{
fontFamily: "sans-serif",
transform: "translate3d(50%, 50%, 0)",
backgroundColor: "white",
padding: "5px",
borderRadius: "3px",
pointerEvents: "none",
}}
>
{name}
</Html>
)}
</group>
)
return <group {...groupProps}>{children}</group>
}

export default ContainerWithTooltip

0 comments on commit ba4a203

Please sign in to comment.