Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refact and move selection in Edit Mode #3

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
59 changes: 6 additions & 53 deletions src/components/3D/Brick.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,16 @@ import React, { useMemo, useEffect, useRef } from "react";
import {
CSSToHex,
getMeasurementsFromDimensions,
base,
getBoundBoxFromMeasures,
createGeometry,
} from "../../utils";
import { Vector3, Box3 } from "three";
import { motion } from "framer-motion-3d";

export const Brick = ({
intersect,
position,
color = "#ff0000",
dimensions = { x: 1, z: 1 },
rotation = 0,
translation = { x: 0, z: 0 },
bricksBoundBox = { current: [] },
uID = "",
onClick = () => {},
Expand All @@ -31,29 +29,8 @@ export const Brick = ({
return createGeometry({ width, height, depth, dimensions });
}, [width, height, depth, dimensions]);

const position = useMemo(() => {
const evenWidth =
rotation === 0 ? dimensions.x % 2 === 0 : dimensions.z % 2 === 0;
const evenDepth =
rotation === 0 ? dimensions.z % 2 === 0 : dimensions.x % 2 === 0;

return new Vector3()
.copy(intersect.point)
.add(intersect.face.normal)
.divide(new Vector3(base, height, base))
.floor()
.multiply(new Vector3(base, height, base))
.add(
new Vector3(
evenWidth ? base : base / 2,
height / 2,
evenDepth ? base : base / 2
)
);
}, [intersect, dimensions.x, dimensions.z, height, rotation]);

useEffect(() => {
const brickBoundingBox = new Box3().setFromObject(brickRef.current);
const brickBoundingBox = getBoundBoxFromMeasures(position, { width, height, depth });

bricksBoundBox.current.push({ uID, brickBoundingBox });

Expand All @@ -67,30 +44,13 @@ export const Brick = ({
}
bricksBoundBox.current = newA;
};
}, [uID, bricksBoundBox]);

const compansate = {
x: dimensions.x % 2 === 0 ? dimensions.x / 2 : (dimensions.x - 1) / 2,
z: dimensions.z % 2 === 0 ? dimensions.z / 2 : (dimensions.z - 1) / 2,
};

const offset = {
x:
Math.sign(translation.x) < 0
? Math.max(translation.x, -compansate.x)
: Math.min(translation.x, compansate.x),
z:
Math.sign(translation.z) < 0
? Math.max(translation.z, -compansate.z)
: Math.min(translation.z, compansate.z),
};
}, [uID, position, dimensions, bricksBoundBox]);

return (
<>
<motion.group
ref={brickRef}
rotation={[0, rotation, 0]}
position={[position.x, Math.abs(position.y), position.z]}
position={[position.x, position.y, position.z]}
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ type: "spring", stiffness: 250, duration: 2 }}
Expand All @@ -104,19 +64,12 @@ export const Brick = ({
userData={{
uID,
dimensions,
offset,
height,
width,
depth,
type: `${dimensions.x}-${dimensions.z}`,
position,
rotation,
translation,
}}
position={[
(offset.x * width) / dimensions.x,
0.5,
(offset.z * depth) / dimensions.z,
]}
onClick={onClick}
geometry={brickGeometry}
onPointerMove={mouseMove}
Expand Down
64 changes: 9 additions & 55 deletions src/components/3D/BrickCursor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,79 +3,34 @@
/* eslint-disable react/prop-types */
/* eslint-disable react/display-name */
import React, { forwardRef, useMemo } from "react";
import { getMeasurementsFromDimensions, createGeometry } from "../../utils";
import { Vector3 } from "three";
import { getMeasurementsFromDimensions, base, CREATE_MODE } from "../../utils";
import { useStore } from "../../store";

export const BrickCursor = forwardRef(
(
{
intersect = {
point: new Vector3(),
face: { normal: { x: 0, y: 0, z: 1 } },
},
position = new Vector3(),
dimensions = { x: 1, z: 1 },
rotation = 0,
translation = { x: 0, z: 0 },
visible = true
},
ref
) => {
const mode = useStore((state) => state.mode);

const visible = mode === CREATE_MODE;

const { height, width, depth } = getMeasurementsFromDimensions(dimensions);

const position = useMemo(() => {
const evenWidth = rotation === 0 ? width % 2 === 0 : depth % 2 === 0;
const evenDepth = rotation === 0 ? depth % 2 === 0 : width % 2 === 0;

return new Vector3()
.copy(intersect.point)
.add(intersect.face.normal)
.divide(new Vector3(base, height, base))
.floor()
.multiply(new Vector3(base, height, base))
.add(
new Vector3(
evenWidth ? base : base / 2,
height / 2,
evenDepth ? base : base / 2
)
);
}, [intersect, height, rotation, width, depth]);

const compansateX =
dimensions.x % 2 === 0 ? dimensions.x / 2 : (dimensions.x - 1) / 2;
const compansateZ =
dimensions.z % 2 === 0 ? dimensions.z / 2 : (dimensions.z - 1) / 2;

const offsetX =
Math.sign(translation.x) < 0
? Math.max(translation.x, -compansateX)
: Math.min(translation.x, compansateX);

const offsetZ =
Math.sign(translation.z) < 0
? Math.max(translation.z, -compansateZ)
: Math.min(translation.z, compansateZ);
const brickGeometry = useMemo(() => {
return createGeometry({ width, height, depth, dimensions, knobDim: 0});
}, [width, height, depth, dimensions]);

return (
<>
<group
ref={ref}
position={[position.x, Math.abs(position.y), position.z]}
rotation={[0, rotation, 0]}
position={[position.x, position.y, position.z]}
visible={visible}
>
<mesh
position={[
(offsetX * width) / dimensions.x,
0,
(offsetZ * width) / dimensions.z,
]}
geometry={brickGeometry}
>
<boxGeometry args={[width, height, depth]} />
<meshBasicMaterial
color={"white"}
transparent={true}
Expand All @@ -85,5 +40,4 @@ export const BrickCursor = forwardRef(
</group>
</>
);
}
);
});
38 changes: 9 additions & 29 deletions src/components/3D/BrickOutline.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import React, { useLayoutEffect, useMemo, useRef } from "react";
import {
createGeometry,
getMeasurementsFromDimensions,
knobSize,
outlineWidth,
KNOB_SIZE,
OUTLINE_WIDTH,
} from "../../utils";
import { BackSide, Object3D } from "three";
import { useStore } from "../../store";
Expand All @@ -26,42 +26,22 @@ const OutlineMesh = ({ meshesData }) => {

const outlineGeometry = useMemo(() => {
return createGeometry({
width: width + outlineWidth * 2,
height: height + outlineWidth * 2,
depth: depth + outlineWidth * 2,
width: width + OUTLINE_WIDTH * 2,
height: height + OUTLINE_WIDTH * 2,
depth: depth + OUTLINE_WIDTH * 2,
dimensions,
knobDim: knobSize + outlineWidth,
knobDim: KNOB_SIZE + OUTLINE_WIDTH,
});
}, [width, height, depth, dimensions]);

useLayoutEffect(() => {
if (!ref.current) return;

meshesData.forEach((meshData, i) => {
const compansate = {
x: dimensions.x % 2 === 0 ? dimensions.x / 2 : (dimensions.x - 1) / 2,
z: dimensions.z % 2 === 0 ? dimensions.z / 2 : (dimensions.z - 1) / 2,
};

const translation = meshData.translation;

const offset = {
x:
Math.sign(translation.x) < 0
? Math.max(translation.x, -compansate.x)
: Math.min(translation.x, compansate.x),
z:
Math.sign(translation.z) < 0
? Math.max(translation.z, -compansate.z)
: Math.min(translation.z, compansate.z),
};

dummy.rotation.set(0, meshData.rotation, 0);
dummy.position.set(
meshData.position.x + (offset.x * width) / dimensions.x,
Math.abs(meshData.position.y),
meshData.position.z + (offset.z * width) / dimensions.z
);
meshData.position.x - OUTLINE_WIDTH,
meshData.position.y - OUTLINE_WIDTH,
meshData.position.z - OUTLINE_WIDTH);
dummy.updateMatrix();
ref.current.setMatrixAt(i, dummy.matrix);
});
Expand Down
38 changes: 9 additions & 29 deletions src/components/3D/ChangeColor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,16 @@ export const ChangeColor = ({ color }) => {

const setBricks = useStore((state) => state.setBricks);

const prevColor = useRef(color);

const deferredColor = useDeferredValue(color);

useEffect(() => {
if (selected.length < 1 || prevColor.current === deferredColor) return;

setBricks((bricks) => {
const updatedBricks = [];

bricks.forEach((brick) => {
const selectedClone = [...selected];
const uID = brick.uID;
for (let i = 0; i < selectedClone.length; i++) {
const selectedUID = selectedClone[i];
if (uID === selectedUID) {
brick.color = deferredColor;
selectedClone.splice(i, 1);
}
}
updatedBricks.push(brick);
});

return updatedBricks;
});

return () => {
prevColor.current = deferredColor;
};
}, [deferredColor, selected, setBricks]);
if (selected.length > 0) {
setBricks((bricks) =>
bricks.map((brick) => ({
...brick,
color: selected.includes(brick.uID) ? color : brick.color
}))
);
}
}, [color]);

return null;
};
66 changes: 66 additions & 0 deletions src/components/3D/MultiBrickCursor.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/* eslint-disable react/no-unknown-property */
/* eslint-disable no-unused-vars */
/* eslint-disable react/prop-types */
/* eslint-disable react/display-name */
import React, { forwardRef, useMemo } from "react";
import { Vector3 } from "three";
import { getMeasurementsFromDimensions, createGeometry } from "../../utils";

export const MultiBrickCursor = forwardRef(
(
{
position = new Vector3(),
anchor = new Vector3(),
bricks = [],
rotate = false,
visible = true
},
ref
) => {
const meshes = useMemo(() => {
return bricks.map((brick) => {
const dimensions = {
x: !rotate ? brick.dimensions.x : brick.dimensions.z,
z: !rotate ? brick.dimensions.z : brick.dimensions.x
}
const { height, width, depth } = getMeasurementsFromDimensions(dimensions);
const brickGeometry = createGeometry({ width, height, depth, dimensions: dimensions, knobDim: 0});

return {
uID: brick.uID,
relPosition: new Vector3()
.copy(brick.position)
.sub(anchor)
.applyAxisAngle(new Vector3(0, 1, 0), rotate ? Math.PI / 2 : 0)
.toArray(),
geometry: brickGeometry
}
})
}, [anchor, bricks, rotate]);

return (
<>
<group
ref={ref}
position={[position.x, position.y, position.z]}
visible={visible}
>
{meshes.map((mesh) => {
return (
<mesh
key={mesh.uID}
position={mesh.relPosition}
geometry={mesh.geometry}
>
<meshBasicMaterial
color={"white"}
transparent={true}
opacity={0.3}
/>
</mesh>
);
})}
</group>
</>
);
});
Loading