Skip to content

Commit 9721053

Browse files
LimHyungTaeclaude
andcommitted
Add per-viewer point-size slider overlay
Every PointCloudViewer now ships a compact "pt 1.00×" control in the top-left of its canvas — drag it to scale the size of every layer's points 0.25×–8×. Sliding scales any per-layer base size, so all coloring modes (flat, normal-RGB, cluster colors) react uniformly. Pointer events on the overlay are stopped so they don't bleed into OrbitControls. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 4fe7a92 commit 9721053

1 file changed

Lines changed: 79 additions & 32 deletions

File tree

web/src/components/PointCloudViewer.tsx

Lines changed: 79 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useMemo } from "react";
1+
import { useEffect, useMemo, useState } from "react";
22
import { Canvas, useThree } from "@react-three/fiber";
33
import { OrbitControls, GizmoHelper, GizmoViewport } from "@react-three/drei";
44
import * as THREE from "three";
@@ -22,6 +22,7 @@ export default function PointCloudViewer({
2222
layers,
2323
background = "#0a0f1a",
2424
}: Props) {
25+
const [sizeMult, setSizeMult] = useState(1);
2526
const { center, radius } = useMemo(() => unionBounds(layers), [layers]);
2627

2728
const initialPos: [number, number, number] = [
@@ -30,40 +31,86 @@ export default function PointCloudViewer({
3031
center.z + radius * 1.4,
3132
];
3233

34+
// Apply the multiplier on the way down so PointsLayer doesn't need to know
35+
// about it. This keeps the slider state local to the viewer and means
36+
// pages stay unchanged.
37+
const scaledLayers = useMemo(
38+
() =>
39+
layers.map((l) => ({
40+
...l,
41+
size: (l.size ?? 0.05) * sizeMult,
42+
})),
43+
[layers, sizeMult],
44+
);
45+
46+
return (
47+
<div className="relative h-full w-full">
48+
<Canvas
49+
style={{ background }}
50+
camera={{
51+
position: initialPos,
52+
fov: 50,
53+
near: Math.max(radius * 0.001, 0.0001),
54+
far: radius * 50 + 1000,
55+
}}
56+
dpr={[1, 2]}
57+
>
58+
<ambientLight intensity={0.6} />
59+
{scaledLayers.map((l, idx) => (
60+
<PointsLayer key={idx} layer={l} />
61+
))}
62+
<axesHelper args={[Math.max(0.05, radius * 0.2)]} />
63+
<ViewFrame
64+
cx={center.x}
65+
cy={center.y}
66+
cz={center.z}
67+
radius={radius}
68+
/>
69+
<OrbitControls enableDamping dampingFactor={0.08} makeDefault />
70+
<GizmoHelper alignment="bottom-right" margin={[64, 64]}>
71+
<GizmoViewport
72+
axisColors={["#ef4444", "#22c55e", "#3b82f6"]}
73+
labelColor="#e2e8f0"
74+
/>
75+
</GizmoHelper>
76+
</Canvas>
77+
78+
<PointSizeControl value={sizeMult} onChange={setSizeMult} />
79+
</div>
80+
);
81+
}
82+
83+
function PointSizeControl({
84+
value,
85+
onChange,
86+
}: {
87+
value: number;
88+
onChange: (v: number) => void;
89+
}) {
3390
return (
34-
<Canvas
35-
style={{ background }}
36-
camera={{
37-
position: initialPos,
38-
fov: 50,
39-
near: Math.max(radius * 0.001, 0.0001),
40-
far: radius * 50 + 1000,
41-
}}
42-
dpr={[1, 2]}
91+
<div
92+
className="absolute left-3 top-3 z-10 flex items-center gap-2 rounded-md border border-[var(--border)] bg-[color:rgba(10,15,26,0.7)] px-2.5 py-1 backdrop-blur-sm"
93+
// Drag/click on the slider should not start an orbit gesture below.
94+
onPointerDown={(e) => e.stopPropagation()}
95+
onWheel={(e) => e.stopPropagation()}
4396
>
44-
<ambientLight intensity={0.6} />
45-
{layers.map((l, idx) => (
46-
<PointsLayer key={idx} layer={l} />
47-
))}
48-
<axesHelper args={[Math.max(0.05, radius * 0.2)]} />
49-
<ViewFrame
50-
cx={center.x}
51-
cy={center.y}
52-
cz={center.z}
53-
radius={radius}
54-
/>
55-
<OrbitControls
56-
enableDamping
57-
dampingFactor={0.08}
58-
makeDefault
97+
<span className="code-font text-[10px] uppercase tracking-wider text-[var(--mut)]">
98+
pt
99+
</span>
100+
<input
101+
type="range"
102+
min={0.25}
103+
max={8}
104+
step={0.05}
105+
value={value}
106+
onChange={(e) => onChange(parseFloat(e.target.value))}
107+
className="w-24 sm:w-28"
108+
aria-label="Point size"
59109
/>
60-
<GizmoHelper alignment="bottom-right" margin={[64, 64]}>
61-
<GizmoViewport
62-
axisColors={["#ef4444", "#22c55e", "#3b82f6"]}
63-
labelColor="#e2e8f0"
64-
/>
65-
</GizmoHelper>
66-
</Canvas>
110+
<span className="code-font min-w-[2.6em] text-right text-[10px] text-[var(--text)]">
111+
{value.toFixed(2)}×
112+
</span>
113+
</div>
67114
);
68115
}
69116

0 commit comments

Comments
 (0)