Skip to content
Merged
137 changes: 133 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,18 @@
"format": "prettier --write ."
},
"dependencies": {
"@customcads/react-sdk": "^0.1.6",
"@customcads/react-sdk": "^0.1.8",
"@icons-pack/react-simple-icons": "^13.8.0",
"@lobehub/icons": "^2.43.1",
"@microsoft/signalr": "^9.0.6",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-progress": "^1.1.8",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
Expand Down
55 changes: 55 additions & 0 deletions src/app/components/cad/editor/hooks/useEditorThreeJS.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useRef } from 'react';
import * as THREE from 'three';
import { CalculateCad } from '@/types/threejs';
import { updateMaterial } from '@/lib/cad/material';
import { boxSize } from '@/lib/cad/three-js';
import * as editor from '@/app/stores/editor';
import * as calculate3D from '@/app/utils/calculate-3D';

export const useEditorThreeJS = (cadId: string) => {
const refs = {
originalScale: useRef<THREE.Vector3>(new THREE.Vector3(0, 0, 0)),
cad: useRef<THREE.Group>(null),
lastTextures: useRef<Map<THREE.Object3D, string>>(new Map()),
};
const actions = editor.getActions(cadId);

const update = {
scene: (cad: THREE.Group) => {
refs.originalScale.current = cad.scale.clone();
refs.cad.current = cad;

const size = boxSize(refs.cad.current);
actions.set.size(size);
},
looks: (texture: string, color?: string) => {
const textures = refs.lastTextures.current;
refs.cad.current?.traverse(
updateMaterial({ texture, color, textures }),
);
},
metrics: ({
volume,
density,
euroPerKg,
scale,
size,
infill,
}: CalculateCad) => {
const volumeMm3 = calculate3D.volumeMm3(volume, scale, size);

const weight = calculate3D.weightGrams(volumeMm3, infill, density);
actions.set.weight(weight);

const cost = calculate3D.costEUR(weight, euroPerKg);
actions.set.cost(cost);

if (refs.cad.current) {
refs.cad.current.scale.copy(refs.originalScale.current);
refs.cad.current.scale.multiplyScalar(scale);
}
},
};

return { update };
};
49 changes: 49 additions & 0 deletions src/app/components/cad/editor/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useQuery } from '@customcads/react-sdk';
import { AppError } from '@/types/errors';
import { getCadType } from '@/lib/cad/get-type';
import { useEditorStore } from '@/app/hooks/stores/useEditorStore';
import { useCadBlobUrl } from '@/app/hooks/features/cads/useCadBlobUrl';
import { useTextures } from '@/app/hooks/features/materials/useTextures';
import Loader from '@/app/components/loading';
import EditorThreeJS from './threejs';

type EditorCadProps = { cadId: string };
const EditorCad = ({ cadId }: EditorCadProps) => {
const { blobUrl: cadBlobUrl, progress } = useCadBlobUrl(cadId, 'Product');
const { data: cad } = useQuery(({ cads }) => cads.single({ id: cadId }));

const materialId = useEditorStore(cadId, (state) => state.materialId);
const textures = useTextures(true);
const texture = textures[materialId];

if (Object.keys(textures).length > 0 && !texture)
throw new AppError({
title: 'Texture Error',
message: 'An error occured during the Rendering process',
tip: 'Reload this page or clear your browser cache.',
});

if (!cad || !cadBlobUrl || !texture) {
return <Loader progress={progress} isCad />;
}

return (
<div className='h-full w-full'>
<EditorThreeJS
texture={texture}
cad={{
id: cadId,
blobUrl: cadBlobUrl,
type: getCadType(cad.contentType as never),
coords: {
cam: cad.camCoordinates,
pan: cad.panCoordinates,
},
volume: cad.volume,
}}
/>
</div>
);
};

export default EditorCad;
55 changes: 55 additions & 0 deletions src/app/components/cad/editor/threejs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useEffect } from 'react';
import * as THREE from 'three';
import { Coordinates } from '@customcads/react-sdk';
import { type AllowedType } from '@/lib/cad/get-type';
import { useThreeJS } from '@/hooks/headless/useThreeJS';
import type { Material } from '@/app/types/material';
import { useEditorStore } from '@/app/hooks/stores/useEditorStore';
import { useEditorThreeJS } from './hooks/useEditorThreeJS';
import Model from '../model';

type Cad = {
id: string;
blobUrl: string;
type: AllowedType | null;
coords: { cam: Coordinates; pan: Coordinates };
volume: number;
};

type Props = { cad: Cad; texture: Material };
const EditorThreeJS = ({ cad, texture }: Props) => {
const { update } = useEditorThreeJS(cad.id);
const store = {
color: useEditorStore(cad.id, (state) => state.color),
size: useEditorStore(cad.id, (state) => state.size),
scale: useEditorStore(cad.id, (state) => state.scale),
infill: useEditorStore(cad.id, (state) => state.infill),
};

const threejs = useThreeJS(
{ url: cad.blobUrl, type: cad.type, coords: cad.coords },
(threejs) => {
update.scene(threejs);
threejs.traverse((child) => {
if (child instanceof THREE.Mesh) {
child.material.map = undefined;
}
});

update.looks(texture.blobUrl, store.color);
update.metrics({ ...cad, ...texture, ...store });
},
);

useEffect(() => {
update.looks(texture.blobUrl, store.color);
}, [texture, store.color]);

useEffect(() => {
update.metrics({ ...cad, ...texture, ...store });
}, [cad.volume, texture.density, store.size, store.scale, store.infill]);

return <Model threejs={threejs} />;
};

export default EditorThreeJS;
29 changes: 29 additions & 0 deletions src/app/components/cad/gallery/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useQuery } from '@customcads/react-sdk';
import { getCadType } from '@/lib/cad/get-type';
import { useCadBlobUrl } from '@/app/hooks/features/cads/useCadBlobUrl';
import Loader from '@/app/components/loading';
import GalleryThreeJS from './threejs';

const GalleryCad = ({ cadId }: { cadId: string }) => {
const { data: cad } = useQuery(({ cads }) => cads.single({ id: cadId }));
const { blobUrl, progress } = useCadBlobUrl(cadId, 'Product');

return (
<div className='relative h-full w-full'>
{!cad || !blobUrl ? (
<Loader isCad progress={progress} />
) : (
<GalleryThreeJS
file={{
url: blobUrl,
type: getCadType(cad.contentType as never),
}}
cam={cad.camCoordinates}
pan={cad.panCoordinates}
/>
)}
</div>
);
};

export default GalleryCad;
Loading