Skip to content

Commit

Permalink
Update media
Browse files Browse the repository at this point in the history
  • Loading branch information
lucemans committed Nov 27, 2024
1 parent aa390da commit 54b6753
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 23 deletions.
5 changes: 5 additions & 0 deletions web/src/api/media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ export const useMedia = (id: number) =>
description: 'test2',
url: '/test.stl',
},
3: {
id: 3,
description: 'test3',
url: '/test2.stl',
},
}[id];
},
});
Expand Down
44 changes: 44 additions & 0 deletions web/src/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, { Component, ReactNode } from 'react';

interface ErrorBoundaryProperties {
children: ReactNode;
}

interface ErrorBoundaryState {
hasError: boolean;
error: Error | null;
}

class ErrorBoundary extends Component<
ErrorBoundaryProperties,
ErrorBoundaryState
> {
constructor(properties: ErrorBoundaryProperties) {
super(properties);
this.state = { hasError: false, error: null };
}

static getDerivedStateFromError(error: Error) {
return { hasError: true, error };
}

componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('Error caught by ErrorBoundary:', error, errorInfo);
}

render() {
if (this.state.hasError) {
return (
<div className="p-4 border-red-500 border-2 rounded-md bg-red-100 h-full">
<p className="font-bold">STL Preview Error</p>
<p>Error loading STL file:</p>
<code>{this.state.error?.message}</code>
</div>
);
}

return this.props.children;
}
}

export { ErrorBoundary };
41 changes: 32 additions & 9 deletions web/src/components/media/MediaPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { FC } from 'react';
import { FC, Suspense, useState } from 'react';
import { match } from 'ts-pattern';

import { useMedia } from '../../api/media';
import { StlPreview } from '../stl_preview/StlPreview';
import { ErrorBoundary } from '../ErrorBoundary';
import { StlPreviewWindow } from '../stl_preview/StlPreview';

export const MediaPreview: FC<{ media_id: number }> = ({ media_id }) => {
const { data: media } = useMedia(media_id);
Expand All @@ -13,9 +14,9 @@ export const MediaPreview: FC<{ media_id: number }> = ({ media_id }) => {
<div className="aspect-video bg-neutral-100 max-w-md w-full border border-neutral-200 rounded-md">
{match(fileType)
.with('webp', () => <ImagePreview media_id={media_id} />)
.with('stl', () => <StlPreview stlUrl={media?.url} />)
.with('stl', () => <StlPreview media_id={media_id} />)
.otherwise(() => (
<div className="p-3">
<div className="p-3 border-orange-500 border-2 rounded-md bg-orange-100 h-full">
<span>Unknown file type</span>
<span>{fileType}</span>
</div>
Expand All @@ -26,14 +27,36 @@ export const MediaPreview: FC<{ media_id: number }> = ({ media_id }) => {

export const ImagePreview: FC<{ media_id: number }> = ({ media_id }) => {
const { data: media } = useMedia(media_id);
const [imageNotFound, setImageNotFound] = useState(false);

return (
<div className="w-full h-full">
<img
src={media?.url}
alt={media?.description}
className="w-full h-full object-contain"
/>
{imageNotFound ? (
<div className="p-3 border-red-500 border-2 rounded-md bg-red-100 h-full">
<p className="font-bold">Image Preview Error</p>
<p>Image not found</p>
<code>{media?.url}</code>
</div>
) : (
<img
src={media?.url}
alt={media?.description}
className="w-full h-full object-contain"
onError={() => setImageNotFound(true)}
/>
)}
</div>
);
};

export const StlPreview: FC<{ media_id: number }> = ({ media_id }) => {
const { data: media } = useMedia(media_id);

return (
<ErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<StlPreviewWindow stlUrl={media?.url} />
</Suspense>
</ErrorBoundary>
);
};
35 changes: 25 additions & 10 deletions web/src/components/stl_preview/StlPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ function Measurements({ geometry }: { geometry: THREE.BufferGeometry }) {
// Create a background plane
const backgroundGeometry = new THREE.PlaneGeometry(12, 6);
const backgroundMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
color: 0xFF_FF_FF,
opacity: 0.5,
transparent: true,
depthTest: false,
Expand All @@ -85,9 +85,10 @@ function Measurements({ geometry }: { geometry: THREE.BufferGeometry }) {

labelGroup.add(backgroundMesh);
labelGroup.add(textMesh);

// Add an offset to the z position to ensure it's above the floor
const labelOffset = new THREE.Vector3(0, 0, 0.5); // Adjust the offset as needed

labelGroup.position.copy(position).add(labelOffset);

// Set render order to ensure labels are rendered on top
Expand Down Expand Up @@ -177,9 +178,9 @@ function Measurements({ geometry }: { geometry: THREE.BufferGeometry }) {
}, [geometry]);

useFrame(() => {
labels.forEach(label => {
for (const label of labels) {
label.lookAt(camera.position);
});
}
});

return (
Expand Down Expand Up @@ -213,28 +214,42 @@ function Model({
const center = new THREE.Vector3();

boundingBox.getCenter(center);

// Swap the Y and Z axes to account for the axis swap
const adjustedCenter = new THREE.Vector3(
center.x,
center.z,
-center.y
);

// Center the geometry based on the adjusted center
geometry.center();

if (onLoad) {
const size = boundingBox.getSize(new THREE.Vector3());
const maxDim = Math.max(size.x, size.y, size.z);

onLoad(maxDim, center);
onLoad(maxDim, adjustedCenter);
}
}
}
}, [geometry, onLoad]);

const width = geometry.boundingBox?.getSize(new THREE.Vector3()).x ?? 0;
const height = geometry.boundingBox?.getSize(new THREE.Vector3()).y ?? 0;
const depth = geometry.boundingBox?.getSize(new THREE.Vector3()).z ?? 0;
const size =
geometry.boundingBox?.getSize(new THREE.Vector3()) ??
new THREE.Vector3();

return (
<>
<mesh
ref={meshReference}
geometry={geometry}
position={[width / 2, depth * 0.75 + 2, -height / 2]}
// Adjust the position to center the model based on the adjusted center
position={[
size.x / 2, // Centered on the X-axis
size.y / 2 + 0.1, // Slightly above the grid
-size.z/2, // Centered on the Z-axis
]}
rotation={[(Math.PI / 2) * 3, 0, 0]}
>
<meshStandardMaterial
Expand All @@ -249,7 +264,7 @@ function Model({
);
}

export const StlPreview = ({ stlUrl }: { stlUrl?: string }) => {
export const StlPreviewWindow = ({ stlUrl }: { stlUrl?: string }) => {
const [cameraDistance, setCameraDistance] = useState(80);
const [modelCenter, setModelCenter] = useState<THREE.Vector3>(
new THREE.Vector3(0, 0, 0)
Expand Down
2 changes: 1 addition & 1 deletion web/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
@tailwind utilities;

.btn {
@apply bg-white text-neutral-800 border border-neutral-200 px-3 py-1 h-fit hover:bg-neutral-50 rounded-md focus:outline focus:outline-2 outline-offset-2 outline-blue-500;
@apply bg-white text-neutral-800 border border-solid border-neutral-200 px-3 py-1 h-fit hover:cursor-pointer hover:bg-neutral-50 rounded-md focus:outline focus:outline-2 outline-offset-2 outline-blue-500;
@apply disabled:bg-neutral-100 disabled:text-neutral-400 disabled:border-neutral-200 disabled:hover:bg-neutral-100 disabled:cursor-not-allowed;
}

Expand Down
18 changes: 16 additions & 2 deletions web/src/routes/item/$itemId/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { createFileRoute, redirect, useParams } from '@tanstack/react-router';
import {
createFileRoute,
Link,
redirect,
useParams,
} from '@tanstack/react-router';

import { formatId, getInstanceSettings } from '../../../api/instance_settings';
import { useApiItemById } from '../../../api/item';
Expand Down Expand Up @@ -33,7 +38,16 @@ export const Route = createFileRoute('/item/$itemId/')({
return (
<div className="p-2 mt-8 mx-auto w-full max-w-4xl space-y-4">
<h1 className="h1">Item {itemId}</h1>
<div className="w-full gap-4 border border-t-4 shadow-sm rounded-md pt-4">
<div className="w-full space-y-4 border border-t-4 shadow-sm rounded-md pt-4">
<div className="p-4 flex justify-end items-center w-full">
<Link
to="/item/$itemId/edit"
params={{ itemId }}
className="btn"
>
<span className="">Edit</span>
</Link>
</div>
<div className="p-4">
<ItemPreview item_id={itemId} />
</div>
Expand Down
2 changes: 1 addition & 1 deletion web/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}', './src/index.css'],
theme: {
extend: {},
},
Expand Down

0 comments on commit 54b6753

Please sign in to comment.