diff --git a/src/viser/_messages.py b/src/viser/_messages.py index d258202d..f050f572 100644 --- a/src/viser/_messages.py +++ b/src/viser/_messages.py @@ -186,8 +186,8 @@ class CameraFrustumProps: """Aspect ratio of the camera (width over height). Synchronized automatically when assigned.""" scale: float """Scale factor for the size of the frustum. Synchronized automatically when assigned.""" - line_thickness: float - """Thickness of the frustum lines. Synchronized automatically when assigned.""" + line_width: float + """Width of the frustum lines. Synchronized automatically when assigned.""" color: Tuple[int, int, int] """Color of the frustum as RGB integers. Synchronized automatically when assigned.""" image_media_type: Optional[Literal["image/jpeg", "image/png"]] diff --git a/src/viser/_scene_api.py b/src/viser/_scene_api.py index b5300a70..c2591327 100644 --- a/src/viser/_scene_api.py +++ b/src/viser/_scene_api.py @@ -739,7 +739,7 @@ def add_camera_frustum( fov: float, aspect: float, scale: float = 0.3, - line_thickness: float | None = None, + line_width: float = 2.0, color: RgbTupleOrArray = (20, 20, 20), image: np.ndarray | None = None, format: Literal["png", "jpeg"] = "jpeg", @@ -747,6 +747,7 @@ def add_camera_frustum( wxyz: tuple[float, float, float, float] | np.ndarray = (1.0, 0.0, 0.0, 0.0), position: tuple[float, float, float] | np.ndarray = (0.0, 0.0, 0.0), visible: bool = True, + *_removed_kwargs, ) -> CameraFrustumHandle: """Add a camera frustum to the scene for visualization. @@ -755,7 +756,7 @@ def add_camera_frustum( and coverage of a camera in the 3D space. Like all cameras in the viser Python API, frustums follow the OpenCV [+Z forward, - +X right, +Y down] convention. fov is vertical in radians; aspect is width over height + +X right, +Y down] convention. fov is vertical in radians; aspect is width over height. Args: name: A scene tree name. Names in the format of /parent/child can be used to @@ -763,8 +764,7 @@ def add_camera_frustum( fov: Field of view of the camera (in radians). aspect: Aspect ratio of the camera (width over height). scale: Scale factor for the size of the frustum. - line_thickness: Thickness of the frustum lines. If not set, - defaults to `0.03 * scale`. + line_width: Width of the frustum lines, in screen space. Defaults to `2.0`. color: Color of the frustum as an RGB tuple. image: Optional image to be displayed on the frustum. format: Format of the provided image ('png' or 'jpeg'). @@ -777,6 +777,12 @@ def add_camera_frustum( Handle for manipulating scene node. """ + if "line_thickness" in _removed_kwargs: + warnings.warn( + "The 'line_thickness' argument has been removed. Please use 'line_width' instead. Note that the units have been changed from world space to screen space.", + DeprecationWarning, + ) + if image is not None: media_type, binary = _encode_image_binary( image, format, jpeg_quality=jpeg_quality @@ -791,9 +797,7 @@ def add_camera_frustum( fov=fov, aspect=aspect, scale=scale, - line_thickness=line_thickness - if line_thickness is not None - else 0.03 * scale, + line_width=line_width, color=_encode_rgb(color), image_media_type=media_type, image_binary=binary, diff --git a/src/viser/client/src/SceneTree.tsx b/src/viser/client/src/SceneTree.tsx index 6565f8d7..8f20a0a2 100644 --- a/src/viser/client/src/SceneTree.tsx +++ b/src/viser/client/src/SceneTree.tsx @@ -282,7 +282,7 @@ function useObjectFactory(message: SceneNodeMessage | undefined): { fov={message.props.fov} aspect={message.props.aspect} scale={message.props.scale} - lineThickness={message.props.line_thickness} + lineWidth={message.props.line_width} color={rgbToInt(message.props.color)} imageBinary={message.props.image_binary} imageMediaType={message.props.image_media_type} diff --git a/src/viser/client/src/ThreeAssets.tsx b/src/viser/client/src/ThreeAssets.tsx index 38b482f7..10c4cb2d 100644 --- a/src/viser/client/src/ThreeAssets.tsx +++ b/src/viser/client/src/ThreeAssets.tsx @@ -1,4 +1,4 @@ -import { Instance, Instances, shaderMaterial } from "@react-three/drei"; +import { Instance, Instances, Line, shaderMaterial } from "@react-three/drei"; import { createPortal, useFrame, useThree } from "@react-three/fiber"; import { Outlines } from "./Outlines"; import React from "react"; @@ -699,7 +699,7 @@ export const CameraFrustum = React.forwardRef< fov: number; aspect: number; scale: number; - lineThickness: number; + lineWidth: number; color: number; imageBinary: Uint8Array | null; imageMediaType: string | null; @@ -722,59 +722,53 @@ export const CameraFrustum = React.forwardRef< let z = 1.0; const volumeScale = Math.cbrt((x * y * z) / 3.0); - x /= volumeScale; - y /= volumeScale; - z /= volumeScale; - - function scaledLineSegments(points: [number, number, number][]) { - points = points.map((xyz) => [xyz[0] * x, xyz[1] * y, xyz[2] * z]); - return [...Array(points.length - 1).keys()].map((i) => ( - - )); - } + x /= volumeScale * props.scale; + y /= volumeScale * props.scale; + z /= volumeScale * props.scale; + + const hoveredRef = React.useContext(HoverableContext)!; + const [isHovered, setIsHovered] = React.useState(false); + + useFrame(() => { + if (hoveredRef.current !== isHovered) { + setIsHovered(hoveredRef.current); + } + }); + + const frustumPoints: [number, number, number][] = [ + // Rectangle. + [-1, -1, 1], + [1, -1, 1], + [1, -1, 1], + [1, 1, 1], + [1, 1, 1], + [-1, 1, 1], + [-1, 1, 1], + [-1, -1, 1], + // Lines to origin. + [-1, -1, 1], + [0, 0, 0], + [0, 0, 0], + [1, -1, 1], + // Lines to origin. + [-1, 1, 1], + [0, 0, 0], + [0, 0, 0], + [1, 1, 1], + // Up direction indicator. + // Don't overlap with the image if the image is present. + [0.0, -1.2, 1.0], + imageTexture === undefined ? [0.0, -0.9, 1.0] : [0.0, -1.0, 1.0], + ].map((xyz) => [xyz[0] * x, xyz[1] * y, xyz[2] * z]); return ( - - - - {scaledLineSegments([ - // Rectangle. - [-1, -1, 1], - [1, -1, 1], - [1, 1, 1], - [-1, 1, 1], - [-1, -1, 1], - ])} - {scaledLineSegments([ - // Lines to origin. - [-1, -1, 1], - [0, 0, 0], - [1, -1, 1], - ])} - {scaledLineSegments([ - // Lines to origin. - [-1, 1, 1], - [0, 0, 0], - [1, 1, 1], - ])} - {scaledLineSegments([ - // Up direction. - [0.0, -1.2, 1.0], - [0.0, -0.9, 1.0], - ])} - + {imageTexture && ( - - - ); -} - export const HoverableContext = React.createContext | null>(null); diff --git a/src/viser/client/src/WebsocketMessages.ts b/src/viser/client/src/WebsocketMessages.ts index 9875cce6..edd034e1 100644 --- a/src/viser/client/src/WebsocketMessages.ts +++ b/src/viser/client/src/WebsocketMessages.ts @@ -100,7 +100,7 @@ export interface CameraFrustumMessage { fov: number; aspect: number; scale: number; - line_thickness: number; + line_width: number; color: [number, number, number]; image_media_type: "image/jpeg" | "image/png" | null; image_binary: Uint8Array | null;