Add axes w/ rotation sync w/ main model viewer

This commit is contained in:
ochafik 2024-12-23 12:07:36 +00:00 committed by Olivier Chafik
commit 7a9e3f927f
3 changed files with 109 additions and 22 deletions

46
axes.scad Normal file
View file

@ -0,0 +1,46 @@
module arrow(length=30, shaft_radius=1, head_radius=2, head_length=5) {
cylinder(h=length-head_length, r=shaft_radius, center=false);
translate([0, 0, length-head_length])
cylinder(h=head_length, r1=head_radius, r2=0, center=false);
}
color("red")
rotate([0, 90, 0])
arrow();
color("green")
rotate([-90, 0, 0])
arrow();
color("blue")
rotate([0, 0, 90])
arrow();
module letter(text)
linear_extrude(1) text(text, halign="center", valign="center");
letter_dist = 38;
union() {
color("red")
translate([letter_dist, 0, 0])
rotate([45, 0, 45])
letter("X");
color("green")
rotate([0, 0, 90])
translate([letter_dist, 0, 0])
rotate([45, 0, -45])
letter("Y");
color("blue")
rotate([0, -90, 0])
translate([letter_dist, 0, 0])
rotate([90+45, 0, 0])
rotate([0, 0, -90])
letter("Z");
}
color("grey")
cube(10, center=true);
color([0, 0, 0, $preview ? 0.05 : 0])
sphere(r=43);

BIN
public/axes.glb Normal file

Binary file not shown.

View file

@ -1,10 +1,7 @@
// Portions of this file are Copyright 2021 Google LLC, and licensed under GPL2+. See COPYING. // Portions of this file are Copyright 2021 Google LLC, and licensed under GPL2+. See COPYING.
import { CSSProperties, useContext, useRef } from 'react'; import { CSSProperties, useContext, useEffect, useRef, useState } from 'react';
import { ModelContext } from './contexts'; import { ModelContext } from './contexts';
import { StlViewer} from "react-stl-viewer";
import { ColorPicker } from 'primereact/colorpicker';
import { defaultModelColor } from '../state/initial-state';
declare global { declare global {
namespace JSX { namespace JSX {
@ -19,18 +16,35 @@ export default function ViewerPanel({className, style}: {className?: string, sty
if (!model) throw new Error('No model'); if (!model) throw new Error('No model');
const state = model.state; const state = model.state;
const modelRef = useRef(); const modelViewerRef = useRef<any>();
const axesViewerRef = useRef<any>();
for (const ref of [modelViewerRef, axesViewerRef]) {
const otherRef = ref === modelViewerRef ? axesViewerRef : modelViewerRef;
useEffect(() => {
function handleCameraChange(e: any) {
if (e.detail.source === 'user-interaction') {
const cameraOrbit = ref.current.getCameraOrbit();
cameraOrbit.radius = otherRef.current.getCameraOrbit().radius;
otherRef.current.cameraOrbit = cameraOrbit.toString();
}
}
ref.current.addEventListener('camera-change', handleCameraChange);
return () => ref.current.removeEventListener('camera-change', handleCameraChange);
}, [ref.current, otherRef.current]);
}
return ( return (
<div className={className} <div className={className}
style={{ style={{
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
position: 'relative',
flex: 1, flex: 1,
width: '100%', width: '100%',
...(style ?? {}) ...(style ?? {})
}}> }}>
{(state.output?.displayFileURL || state.output?.outFile && state.output.outFile.name.endsWith('.glb') && state.output?.outFileURL) && (
<model-viewer <model-viewer
orientation="0deg -90deg 0deg" orientation="0deg -90deg 0deg"
src={state.output?.displayFileURL ?? state.output?.outFileURL ?? ''} src={state.output?.displayFileURL ?? state.output?.outFileURL ?? ''}
@ -43,10 +57,37 @@ export default function ViewerPanel({className, style}: {className?: string, sty
min-camera-orbit="auto 0deg auto" min-camera-orbit="auto 0deg auto"
camera-controls camera-controls
ar ar
ref={(ref: any) => { ref={modelViewerRef}
modelRef.current = ref; >
<span slot="progress-bar"></span>
</model-viewer>
{state.view.showAxes && (
<model-viewer
orientation="0deg -90deg 0deg"
src="./axes.glb"
style={{
position: 'absolute',
bottom: 0,
right: 0,
zIndex: 10,
height: '200px',
width: '200px',
}} }}
/> loading="eager"
interpolation-decay="0"
environment-image="./skybox-lights.jpg"
max-camera-orbit="auto 180deg auto"
min-camera-orbit="auto 0deg auto"
orbit-sensitivity="5"
interaction-prompt="none"
disable-zoom
camera-controls="false"
disable-tap
disable-pan
ref={axesViewerRef}
>
<span slot="progress-bar"></span>
</model-viewer>
)} )}
</div> </div>
) )