Skip to content

Commit

Permalink
fix: added selection coloring and an info box for the selected object (
Browse files Browse the repository at this point in the history
  • Loading branch information
Krande authored Jan 31, 2024
1 parent 6e3c7d9 commit 7869156
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 35 deletions.
Binary file modified src/ada/visit/rendering/resources/index.zip
Binary file not shown.
5 changes: 3 additions & 2 deletions src/frontend/src/components/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ const NavBar: React.FC<NavBarProps> = ({setIsNavBarVisible, sendMessage}) => {
>Show ColorLegend
</button>
<button
className={"bg-blue-700 hover:bg-blue-700/50 text-white font-bold py-2 px-4 ml-2 rounded"}
className={"bg-blue-700 hidden hover:bg-blue-700/50 text-white font-bold py-2 px-4 ml-2 rounded"}
onClick={connect_to_jupyter}
>Jupyter Test</button>

<button
className={"absolute hidden bottom-0 left-0 bg-blue-700 hover:bg-blue-700/50 text-white font-bold py-2 px-4 rounded"}
className={"absolute bottom-0 left-0 bg-blue-700 hover:bg-blue-700/50 text-white font-bold py-2 px-4 rounded"}
onClick={() => setIsNavBarVisible(false)}
>
Expand Down
11 changes: 11 additions & 0 deletions src/frontend/src/components/viewer/AnimationControls.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {useState} from 'react';
import {useAnimationStore} from '../../state/animationStore';
import {handleAnimationChange, stopAnimation, togglePlayPause} from "../../utils/animation_utils";
import {toggle_info_panel} from "../../utils/info_panel_utils";

const playpause_svg = <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5}
stroke="currentColor" className="w-6 h-6">
Expand All @@ -14,6 +15,12 @@ const stop_svg = <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0
d="M5.25 7.5A2.25 2.25 0 0 1 7.5 5.25h9a2.25 2.25 0 0 1 2.25 2.25v9a2.25 2.25 0 0 1-2.25 2.25h-9a2.25 2.25 0 0 1-2.25-2.25v-9Z"/>
</svg>

const info_svg = <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5}
stroke="currentColor" className="w-6 h-6">
<path strokeLinecap="round" strokeLinejoin="round"
d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z"/>
</svg>

const AnimationControls = () => {
const {
animations,
Expand All @@ -30,6 +37,10 @@ const AnimationControls = () => {

return (
<div className={"w-full h-full flex flex-row space-x-2"}>
<button
className={"bg-blue-700 hover:bg-blue-700/50 text-white font-bold py-2 px-4 ml-2 rounded"}
onClick={toggle_info_panel}
>{info_svg}</button>
<select
className={"text-black font-bold py-2 px-4 ml-2 rounded w-60"}
value={selectedAnimation}
Expand Down
9 changes: 7 additions & 2 deletions src/frontend/src/components/viewer/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ import {useNavBarStore} from "../../state/navBarStore";
import AnimationControls from "./AnimationControls";
import {PerspectiveCamera} from "three";
import {handleMeshEmptySpace, handleMeshSelected} from "../../utils/mesh_handling";
import ObjectInfoBox from "./objectInfo";
import {useObjectInfoStore} from "../../state/objectInfoStore";

const cameraProps = new PerspectiveCamera(60, 1.0, 0.1, 10000);
const blenderBackgroundColor = "#393939"; // Approximation of Blender's background color

const CanvasComponent = () => {
const {modelUrl} = useModelStore();
const {showPerf} = useNavBarStore(); // use showPerf and setShowPerf from useNavBarStore
const {show} = useObjectInfoStore()


useEffect(() => {
Expand All @@ -28,19 +31,21 @@ const CanvasComponent = () => {
cameraProps.aspect = parentWidth && parentHeight ? parentWidth / parentHeight : window.innerWidth / window.innerHeight;
cameraProps.position.set(5, 5, 5);
cameraProps.lookAt(0, 0, 0);

}, []);


return (
<div className={"relative w-full h-full"}>
<div className={"absolute left-0 top-0 z-10 py-2"}>
<div className={"absolute left-0 top-0 z-10 py-2 flex flex-col"}>
<AnimationControls/>
<div className={show ? "hidden": ""}><ObjectInfoBox/></div>

</div>
<div className="absolute right-5 top-80 z-10">
<ColorLegend/>
</div>


<div id={"canvasParent"} className={"absolute w-full h-full"}>
<Canvas
camera={cameraProps}
Expand Down
13 changes: 10 additions & 3 deletions src/frontend/src/components/viewer/ThreeModel.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Model.tsx
import React, {useEffect} from 'react';
import {useGLTF} from '@react-three/drei';
import {useFrame} from "@react-three/fiber";
import {useFrame, useThree} from "@react-three/fiber";
import * as THREE from 'three'
import {GLTFResult, ModelProps} from "../../state/modelInterfaces";
import {useAnimationStore} from '../../state/animationStore';
Expand All @@ -10,16 +10,23 @@ import {handleMeshSelected} from "../../utils/mesh_handling";


const Model: React.FC<ModelProps> = ({url, onMeshSelected}) => {
const {raycaster} = useThree();
const {scene, animations} = useGLTF(url, false) as unknown as GLTFResult;
const {action, setCurrentKey, setSelectedAnimation} = useAnimationStore();

useAnimationEffects(animations, scene);

useEffect(() => {
raycaster.params.Line.threshold = 0.01;
scene.traverse((object) => {
if (object instanceof THREE.Mesh) {
object.material.side = THREE.DoubleSide;
console.log(object.material)
// Check if the geometry of the mesh is an instance of THREE.BufferGeometry or THREE.Geometry
if (object.geometry instanceof THREE.BufferGeometry) {
// This is a volume-bounding mesh, do nothing
} else {
// This is a flat surface mesh, set the material to DoubleSide
object.material.side = THREE.DoubleSide;
}
}
});

Expand Down
22 changes: 22 additions & 0 deletions src/frontend/src/components/viewer/objectInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import {useObjectInfoStore} from '../../state/objectInfoStore';

const ObjectInfoBox = () => {
const {name, faceIndex} = useObjectInfoStore();

return (
<div className="bg-gray-400 bg-opacity-50 rounded p-2 m-2">
<h2 className={"font-bold"}>Selected Object Info</h2>
<div className="table-row">
<div className="table-cell w-24">Name:</div>
<div className="table-cell w-48">{name}</div>
</div>
<div className="table-row">
<div className="table-cell w-24">Face Index:</div>
<div className="table-cell w-48">{faceIndex}</div>
</div>
</div>
);
};

export default ObjectInfoBox;
19 changes: 19 additions & 0 deletions src/frontend/src/state/objectInfoStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { create } from 'zustand';

type ObjectInfoState = {
name: string | null;
setName: (name: string | null) => void;
faceIndex: number | null;
setFaceIndex: (faceIndex: number | null) => void;
show: boolean;
toggle: () => void;
};

export const useObjectInfoStore = create<ObjectInfoState>((set) => ({
name: null,
setName: (name) => set(() => ({ name })),
faceIndex: null,
setFaceIndex: (faceIndex) => set(() => ({ faceIndex })),
show: false,
toggle: () => set((state) => ({ show: !state.show })),
}));
8 changes: 4 additions & 4 deletions src/frontend/src/state/selectedObjectStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import * as THREE from 'three';
type SelectedObjectState = {
selectedObject: THREE.Mesh | null;
setSelectedObject: (mesh: THREE.Mesh | null) => void;
originalColor: THREE.Color | null;
setOriginalColor: (color: THREE.Color) => void;
originalMaterial: THREE.MeshBasicMaterial | null;
setOriginalMaterial: (material: THREE.MeshBasicMaterial | null) => void;
};

export const useSelectedObjectStore = create<SelectedObjectState>((set) => ({
selectedObject: null,
setSelectedObject: (mesh) => set(() => ({selectedObject: mesh})),
originalColor: null,
setOriginalColor: (color: THREE.Color) => set(() => ({originalColor: color})),
originalMaterial: null,
setOriginalMaterial: (material: THREE.MeshBasicMaterial | null) => set(() => ({originalMaterial: material})),
}));
5 changes: 5 additions & 0 deletions src/frontend/src/utils/info_panel_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {useObjectInfoStore} from "../state/objectInfoStore";

export function toggle_info_panel() {
useObjectInfoStore.getState().toggle();
}
59 changes: 35 additions & 24 deletions src/frontend/src/utils/mesh_handling.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,52 @@
import {useSelectedObjectStore} from "../state/selectedObjectStore";
import * as THREE from "three";
import {ThreeEvent} from "@react-three/fiber";
import {useObjectInfoStore} from "../state/objectInfoStore";


const selectedMaterial = new THREE.MeshStandardMaterial({color: 'blue', side: THREE.DoubleSide});
const defaultMaterial = new THREE.MeshStandardMaterial({color: 'white', side: THREE.DoubleSide});


function deselectObject() {
const selectedObject = useSelectedObjectStore.getState().selectedObject;
const originalMaterial = useSelectedObjectStore.getState().originalMaterial;
if (selectedObject) {
selectedObject.material = originalMaterial? originalMaterial : defaultMaterial;
useSelectedObjectStore.getState().setOriginalMaterial(null);
useSelectedObjectStore.getState().setSelectedObject(null);
}
}


export function handleMeshSelected(event: ThreeEvent<PointerEvent>) {
event.stopPropagation();

const selectedObject = useSelectedObjectStore.getState().selectedObject;
const originalColor = useSelectedObjectStore.getState().originalColor;
const originalMaterial = useSelectedObjectStore.getState().originalMaterial;
const mesh = event.object as THREE.Mesh;

if (selectedObject !== mesh) {
if (selectedObject) {
(selectedObject.material as THREE.MeshBasicMaterial).color.set(originalColor || 'white');
selectedObject.material = originalMaterial? originalMaterial : defaultMaterial;
}

const material = mesh.material as THREE.MeshBasicMaterial;
useSelectedObjectStore.getState().setOriginalColor(material.color);
useSelectedObjectStore.getState().setOriginalMaterial(mesh.material? material : null);
useSelectedObjectStore.getState().setSelectedObject(mesh);
const meshInfo = {
name: mesh.name,
materialName: material.name,
intersectionPoint: event.point,
faceIndex: event.faceIndex || 0,
meshClicked: true,
};

console.log('mesh clicked');
console.log(meshInfo);
console.log(event);

// Update the object info store
useObjectInfoStore.getState().setName(mesh.name);
useObjectInfoStore.getState().setFaceIndex(event.faceIndex || 0);

// Create a new material for the selected mesh
mesh.material = selectedMaterial;
} else {
deselectObject();
}

}

export function handleMeshEmptySpace(event: MouseEvent) {
const selectedObject = useSelectedObjectStore.getState().selectedObject;
const originalColor = useSelectedObjectStore.getState().originalColor;

console.log('click on empty space');
if (selectedObject) {
console.log(`deselecting object. Reverting to original color ${originalColor}`);
(selectedObject.material as THREE.MeshBasicMaterial).color.set(originalColor || 'white');
useSelectedObjectStore.getState().setSelectedObject(null);
}
event.stopPropagation();
deselectObject();
}

0 comments on commit 7869156

Please sign in to comment.