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;