Skip to content
Open
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
35 changes: 25 additions & 10 deletions src/CadViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,13 @@ const CadViewerInner = (props: any) => {
return stored === "true"
})
const [cameraPreset, setCameraPreset] = useState<CameraPreset>("Custom")
const { visibility, toggleLayer } = useLayerVisibility()

const [shouldUseOrthographicCamera, setShouldUseOrthographicCamera] =
useState(() => {
const stored = window.localStorage.getItem(
"cadViewerUseOrthographicCamera",
)
return stored === "true"
})
const cameraControllerRef = useRef<CameraController | null>(null)
const externalCameraControllerReady = props.onCameraControllerReady as
| ((controller: CameraController | null) => void)
Expand Down Expand Up @@ -74,6 +79,10 @@ const CadViewerInner = (props: any) => {
setAutoRotateUserToggled(true)
}, [])

const toggleOrthographicCamera = useCallback(() => {
setShouldUseOrthographicCamera((prev) => !prev)
}, [])

const downloadGltf = useGlobalDownloadGltf()

const closeMenu = useCallback(() => {
Expand All @@ -84,11 +93,8 @@ const CadViewerInner = (props: any) => {
(controller: CameraController | null) => {
cameraControllerRef.current = controller
externalCameraControllerReady?.(controller)
if (controller && cameraPreset !== "Custom") {
controller.animateToPreset(cameraPreset)
}
},
[cameraPreset, externalCameraControllerReady],
[externalCameraControllerReady],
)

const { handleCameraPresetSelect } = useCameraPreset({
Expand Down Expand Up @@ -123,13 +129,15 @@ const CadViewerInner = (props: any) => {
)
}, [autoRotateUserToggled])

const viewerKey = props.circuitJson
? JSON.stringify(props.circuitJson)
: undefined
useEffect(() => {
window.localStorage.setItem(
"cadViewerUseOrthographicCamera",
String(shouldUseOrthographicCamera),
)
}, [shouldUseOrthographicCamera])

return (
<div
key={viewerKey}
ref={containerRef}
style={{
width: "100%",
Expand All @@ -149,13 +157,15 @@ const CadViewerInner = (props: any) => {
autoRotateDisabled={props.autoRotateDisabled || !autoRotate}
onUserInteraction={handleUserInteraction}
onCameraControllerReady={handleCameraControllerReady}
shouldUseOrthographicCamera={shouldUseOrthographicCamera}
/>
) : (
<CadViewerManifold
{...props}
autoRotateDisabled={props.autoRotateDisabled || !autoRotate}
onUserInteraction={handleUserInteraction}
onCameraControllerReady={handleCameraControllerReady}
shouldUseOrthographicCamera={shouldUseOrthographicCamera}
/>
)}
<div
Expand All @@ -181,6 +191,7 @@ const CadViewerInner = (props: any) => {
engine={engine}
cameraPreset={cameraPreset}
autoRotate={autoRotate}
shouldUseOrthographicCamera={shouldUseOrthographicCamera}
onEngineSwitch={(newEngine) => {
setEngine(newEngine)
closeMenu()
Expand All @@ -190,6 +201,10 @@ const CadViewerInner = (props: any) => {
toggleAutoRotate()
closeMenu()
}}
onOrthographicToggle={() => {
toggleOrthographicCamera()
closeMenu()
}}
onDownloadGltf={() => {
downloadGltf()
closeMenu()
Expand Down
56 changes: 52 additions & 4 deletions src/CadViewerContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,24 @@ export type {
CameraPreset,
} from "./hooks/useCameraController"

declare global {
interface Window {
TSCI_MAIN_CAMERA_ROTATION: THREE.Euler
TSCI_MAIN_CAMERA_QUATERNION: THREE.Quaternion
}
}

if (typeof window !== "undefined") {
window.TSCI_MAIN_CAMERA_ROTATION ??= new THREE.Euler(0, 0, 0)
window.TSCI_MAIN_CAMERA_QUATERNION ??= new THREE.Quaternion()
}

export const RotationTracker = () => {
const { camera } = useThree()
useFrame(() => {
if (camera) {
window.TSCI_MAIN_CAMERA_ROTATION = camera.rotation
window.TSCI_MAIN_CAMERA_ROTATION.copy(camera.rotation)
window.TSCI_MAIN_CAMERA_QUATERNION.copy(camera.quaternion)
}
})

Expand All @@ -41,6 +54,7 @@ interface Props {
boardCenter?: { x: number; y: number }
onUserInteraction?: () => void
onCameraControllerReady?: (controller: CameraController | null) => void
shouldUseOrthographicCamera?: boolean
}

export const CadViewerContainer = forwardRef<
Expand All @@ -57,6 +71,7 @@ export const CadViewerContainer = forwardRef<
boardCenter,
onUserInteraction,
onCameraControllerReady,
shouldUseOrthographicCamera,
},
ref,
) => {
Expand Down Expand Up @@ -91,11 +106,34 @@ export const CadViewerContainer = forwardRef<
}, [orbitTarget])

const { cameraAnimatorProps, handleControlsChange } = useCameraController({
isOrthographic: shouldUseOrthographicCamera ?? false,
defaultTarget,
initialCameraPosition,
onCameraControllerReady,
})

const orthographicFrustumSize = useMemo(() => {
if (boardDimensions) {
const width = boardDimensions.width ?? 0
const height = boardDimensions.height ?? 0
const maxDimension = Math.max(width, height)
return Math.max(maxDimension * 1.5, 10)
}
const [x, y, z] = initialCameraPosition
const maxComponent = Math.max(Math.abs(x), Math.abs(y), Math.abs(z))
return Math.max(maxComponent * 2, 10)
}, [initialCameraPosition, boardDimensions])

const mutableInitialCameraPosition = useMemo(
() =>
[
initialCameraPosition[0],
initialCameraPosition[1],
initialCameraPosition[2],
] as [number, number, number],
[initialCameraPosition],
)

return (
<div style={{ position: "relative", width: "100%", height: "100%" }}>
<div
Expand All @@ -120,7 +158,13 @@ export const CadViewerContainer = forwardRef<
<Canvas
ref={ref}
scene={{ up: new THREE.Vector3(0, 0, 1) }}
camera={{ up: [0, 0, 1], position: initialCameraPosition }}
camera={{
up: [0, 0, 1],
position: mutableInitialCameraPosition,
type: shouldUseOrthographicCamera ? "orthographic" : "perspective",
frustumSize: orthographicFrustumSize,
}}
// The new prop from main:
onCreated={({ camera }) => {
cameraRef.current = camera
if (!restoredOnceRef.current && controlsRef.current) {
Expand All @@ -130,7 +174,7 @@ export const CadViewerContainer = forwardRef<
)
if (restored) restoredOnceRef.current = true
}
// If nothing to restore, persist the initial state once controls exist
// If nothing to restore, persist the initial state once controls exist
if (controlsRef.current && !restoredOnceRef.current) {
setTimeout(() => {
if (cameraRef.current && controlsRef.current) {
Expand All @@ -140,10 +184,14 @@ export const CadViewerContainer = forwardRef<
}
}}
>
<CameraAnimator {...cameraAnimatorProps} />
<CameraAnimator
key={shouldUseOrthographicCamera ? "orthographic" : "perspective"}
{...cameraAnimatorProps}
/>
<RotationTracker />
{isInteractionEnabled && (
<OrbitControls
key={shouldUseOrthographicCamera ? "orthographic" : "perspective"}
autoRotate={!autoRotateDisabled}
autoRotateSpeed={1}
onStart={onUserInteraction}
Expand Down
3 changes: 3 additions & 0 deletions src/CadViewerJscad.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ interface Props {
clickToInteractEnabled?: boolean
onUserInteraction?: () => void
onCameraControllerReady?: (controller: CameraController | null) => void
shouldUseOrthographicCamera?: boolean
}

export const CadViewerJscad = forwardRef<
Expand All @@ -46,6 +47,7 @@ export const CadViewerJscad = forwardRef<
clickToInteractEnabled,
onUserInteraction,
onCameraControllerReady,
shouldUseOrthographicCamera,
},
ref,
) => {
Expand Down Expand Up @@ -134,6 +136,7 @@ export const CadViewerJscad = forwardRef<
boardCenter={boardCenter}
onUserInteraction={onUserInteraction}
onCameraControllerReady={onCameraControllerReady}
shouldUseOrthographicCamera={shouldUseOrthographicCamera}
>
{boardStls.map(({ stlData, color, layerType }, index) => (
<VisibleSTLModel
Expand Down
3 changes: 3 additions & 0 deletions src/CadViewerManifold.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ type CadViewerManifoldProps = {
clickToInteractEnabled?: boolean
onUserInteraction?: () => void
onCameraControllerReady?: (controller: CameraController | null) => void
shouldUseOrthographicCamera?: boolean
} & (
| { circuitJson: AnyCircuitElement[]; children?: React.ReactNode }
| { circuitJson?: never; children: React.ReactNode }
Expand All @@ -134,6 +135,7 @@ const CadViewerManifold: React.FC<CadViewerManifoldProps> = ({
onUserInteraction,
children,
onCameraControllerReady,
shouldUseOrthographicCamera,
}) => {
const childrenCircuitJson = useConvertChildrenToCircuitJson(children)
const circuitJson = useMemo(() => {
Expand Down Expand Up @@ -313,6 +315,7 @@ try {
boardCenter={boardCenter}
onUserInteraction={onUserInteraction}
onCameraControllerReady={onCameraControllerReady}
shouldUseOrthographicCamera={shouldUseOrthographicCamera}
>
<BoardMeshes
geometryMeshes={geometryMeshes}
Expand Down
27 changes: 27 additions & 0 deletions src/components/ContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ interface ContextMenuProps {
engine: "jscad" | "manifold"
cameraPreset: CameraPreset
autoRotate: boolean
shouldUseOrthographicCamera: boolean
onEngineSwitch: (engine: "jscad" | "manifold") => void
onCameraPresetSelect: (preset: CameraPreset) => void
onAutoRotateToggle: () => void
onOrthographicToggle: () => void
onDownloadGltf: () => void
}

Expand Down Expand Up @@ -102,9 +104,11 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
engine,
cameraPreset,
autoRotate,
shouldUseOrthographicCamera,
onEngineSwitch,
onCameraPresetSelect,
onAutoRotateToggle,
onOrthographicToggle,
onDownloadGltf,
}) => {
const [cameraSubOpen, setCameraSubOpen] = useState(false)
Expand Down Expand Up @@ -229,6 +233,29 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
</span>
</DropdownMenu.Item>

<DropdownMenu.Item
style={{
...itemStyles,
...itemPaddingStyles,
backgroundColor:
hoveredItem === "orthographic" ? "#404040" : "transparent",
}}
onSelect={(e) => e.preventDefault()}
onPointerDown={(e) => {
e.preventDefault()
onOrthographicToggle()
}}
onMouseEnter={() => setHoveredItem("orthographic")}
onMouseLeave={() => setHoveredItem(null)}
>
<span style={iconContainerStyles}>
{shouldUseOrthographicCamera && <CheckIcon />}
</span>
<span style={{ display: "flex", alignItems: "center" }}>
Orthographic camera
</span>
</DropdownMenu.Item>

{/* Appearance Menu */}
<AppearanceMenu />

Expand Down
Loading
Loading