imwithye commited on
Commit
20ba9a1
·
1 Parent(s): 9d0c4b5

refactor code

Browse files
src/components/cube-piece.tsx CHANGED
@@ -1,10 +1,10 @@
1
  "use client";
2
 
3
  import { RoundedBox } from "@react-three/drei";
4
- import { useRef, useState } from "react";
5
  import { FacingDirection, Rotations } from "./consts";
6
  import { Mesh } from "three";
7
- import { useCubesContext } from "@/contexts/cubes-context";
8
 
9
  // Standard Rubik's cube colors
10
  const CUBE_COLORS = {
@@ -26,8 +26,11 @@ export const CubePiece = ({ roughness, initialPosition }: CubePieceProps) => {
26
  const [position] = useState<[number, number, number]>([x, y, z]);
27
 
28
  const meshRef = useRef<Mesh | null>(null);
29
- const { addCube } = useCubesContext();
30
- addCube(meshRef);
 
 
 
31
 
32
  const visibleFaces: Record<FacingDirection, boolean> = {
33
  front: z > 0,
 
1
  "use client";
2
 
3
  import { RoundedBox } from "@react-three/drei";
4
+ import { useEffect, useRef, useState } from "react";
5
  import { FacingDirection, Rotations } from "./consts";
6
  import { Mesh } from "three";
7
+ import { rotationController } from "./rotation-controller";
8
 
9
  // Standard Rubik's cube colors
10
  const CUBE_COLORS = {
 
26
  const [position] = useState<[number, number, number]>([x, y, z]);
27
 
28
  const meshRef = useRef<Mesh | null>(null);
29
+ useEffect(() => {
30
+ if (meshRef.current) {
31
+ rotationController.addCube(meshRef.current);
32
+ }
33
+ }, [meshRef]);
34
 
35
  const visibleFaces: Record<FacingDirection, boolean> = {
36
  front: z > 0,
src/components/rotation-controller.ts ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { FacingDirection, RotationStep } from "./consts";
2
+ import { Group, Mesh } from "three";
3
+
4
+ export class RotationController {
5
+ private static instance: RotationController;
6
+
7
+ private cubeGroup?: Group;
8
+ private cubes: Array<Mesh>;
9
+ private cubeSpeed: number;
10
+ private rotationSteps: Array<RotationStep>;
11
+ private rotatingStep: RotationStep | null;
12
+ private rotatingGroup: Group;
13
+
14
+ constructor() {
15
+ this.cubes = [];
16
+ this.cubeSpeed = 2;
17
+ this.rotationSteps = [];
18
+ this.rotatingStep = null;
19
+ this.rotatingGroup = new Group();
20
+ }
21
+
22
+ private rotate(step: RotationStep, group: Group, delta: number) {
23
+ let sign = 0;
24
+ let axis: "x" | "y" | "z" = "x";
25
+ switch (step.faceDirection) {
26
+ case "front":
27
+ sign = step.direction === "clockwise" ? -1 : 1;
28
+ axis = "z";
29
+ break;
30
+ case "back":
31
+ sign = step.direction === "clockwise" ? 1 : -1;
32
+ axis = "z";
33
+ break;
34
+ case "left":
35
+ sign = step.direction === "clockwise" ? 1 : -1;
36
+ axis = "x";
37
+ break;
38
+ case "right":
39
+ sign = step.direction === "clockwise" ? -1 : 1;
40
+ axis = "x";
41
+ break;
42
+ case "top":
43
+ sign = step.direction === "clockwise" ? -1 : 1;
44
+ axis = "y";
45
+ break;
46
+ case "bottom":
47
+ sign = step.direction === "clockwise" ? 1 : -1;
48
+ axis = "y";
49
+ break;
50
+ }
51
+
52
+ group.rotation[axis] += sign * delta * this.cubeSpeed;
53
+ if (Math.abs(group.rotation[axis]) > Math.PI / 2) {
54
+ group.rotation[axis] = (Math.PI / 2) * sign;
55
+ return true;
56
+ }
57
+ return false;
58
+ }
59
+
60
+ static getInstance() {
61
+ if (!RotationController.instance) {
62
+ RotationController.instance = new RotationController();
63
+ }
64
+ return RotationController.instance;
65
+ }
66
+
67
+ setCubeGroup(cubeGroup: Group) {
68
+ this.cubeGroup = cubeGroup;
69
+ }
70
+
71
+ addCube(cube: Mesh) {
72
+ if (!this.cubes.includes(cube)) {
73
+ this.cubes.push(cube);
74
+ }
75
+ }
76
+
77
+ getCubes(faceDirection: FacingDirection) {
78
+ switch (faceDirection) {
79
+ case "front":
80
+ return this.cubes.filter((m) => m.position.z > 0);
81
+ case "back":
82
+ return this.cubes.filter((m) => m.position.z < 0);
83
+ case "left":
84
+ return this.cubes.filter((m) => m.position.x < 0);
85
+ case "right":
86
+ return this.cubes.filter((m) => m.position.x > 0);
87
+ case "top":
88
+ return this.cubes.filter((m) => m.position.y > 0);
89
+ case "bottom":
90
+ return this.cubes.filter((m) => m.position.y < 0);
91
+ }
92
+ }
93
+
94
+ setCubeSpeed(cubeSpeed: number) {
95
+ this.cubeSpeed = cubeSpeed;
96
+ }
97
+
98
+ addRotationStep(...step: Array<RotationStep>) {
99
+ this.rotationSteps.push(...step);
100
+ }
101
+
102
+ frameCallback(state: unknown, delta: number) {
103
+ if (!this.cubeGroup) return;
104
+ if (this.rotationSteps.length === 0 && !this.rotatingStep) return;
105
+ if (!this.rotatingStep) {
106
+ const step = this.rotationSteps.shift();
107
+ if (!step) return;
108
+ this.rotatingStep = step;
109
+ const cubes = this.getCubes(step.faceDirection);
110
+ this.rotatingGroup = new Group();
111
+ this.cubeGroup?.add(this.rotatingGroup);
112
+ cubes.forEach((cube) => this.rotatingGroup.attach(cube));
113
+ }
114
+
115
+ const done = this.rotate(this.rotatingStep, this.rotatingGroup, delta);
116
+ if (done) {
117
+ this.rotatingStep = null;
118
+ const children = [...this.rotatingGroup.children];
119
+ children.forEach((child) => this.cubeGroup?.attach(child));
120
+ this.cubeGroup?.remove(this.rotatingGroup);
121
+ }
122
+ }
123
+ }
124
+
125
+ export const rotationController = RotationController.getInstance();
src/components/rotator.tsx CHANGED
@@ -1,14 +1,7 @@
1
- import { useCubesContext } from "@/contexts/cubes-context";
2
  import { FacingDirection, RotationStep } from "./consts";
3
  import { RotationPanel } from "./rotation-panel";
4
- import { Group } from "three";
5
- import {
6
- forwardRef,
7
- Fragment,
8
- useImperativeHandle,
9
- useMemo,
10
- useRef,
11
- } from "react";
12
  import { useFrame } from "@react-three/fiber";
13
 
14
  export type RotatorRef = {
@@ -21,81 +14,14 @@ type RotatorProps = {
21
 
22
  export const Rotator = forwardRef<RotatorRef, RotatorProps>(
23
  ({ cubeSpeed }: RotatorProps, ref) => {
24
- const cubeSpeedRef = useRef(cubeSpeed);
25
- cubeSpeedRef.current = cubeSpeed;
26
- const { getCubes, cubeGroupRef } = useCubesContext();
27
- const rotationSteps = useRef<Array<RotationStep>>([]);
28
 
29
  useImperativeHandle(ref, () => ({
30
  rotate: (steps: Array<RotationStep>) =>
31
- rotationSteps.current.push(...steps),
32
  }));
33
 
34
- const frameCallback = useMemo(() => {
35
- let rotationStep: RotationStep | null = null;
36
- let rotatingGroup = new Group();
37
-
38
- const rotate = (step: RotationStep, group: Group, delta: number) => {
39
- let sign = 0;
40
- let axis: "x" | "y" | "z" = "x";
41
- switch (step.faceDirection) {
42
- case "front":
43
- sign = step.direction === "clockwise" ? -1 : 1;
44
- axis = "z";
45
- break;
46
- case "back":
47
- sign = step.direction === "clockwise" ? 1 : -1;
48
- axis = "z";
49
- break;
50
- case "left":
51
- sign = step.direction === "clockwise" ? 1 : -1;
52
- axis = "x";
53
- break;
54
- case "right":
55
- sign = step.direction === "clockwise" ? -1 : 1;
56
- axis = "x";
57
- break;
58
- case "top":
59
- sign = step.direction === "clockwise" ? -1 : 1;
60
- axis = "y";
61
- break;
62
- case "bottom":
63
- sign = step.direction === "clockwise" ? 1 : -1;
64
- axis = "y";
65
- break;
66
- }
67
-
68
- group.rotation[axis] += sign * delta * cubeSpeedRef.current;
69
- if (Math.abs(group.rotation[axis]) > Math.PI / 2) {
70
- group.rotation[axis] = (Math.PI / 2) * sign;
71
- return true;
72
- }
73
- return false;
74
- };
75
-
76
- return (state: unknown, delta: number) => {
77
- if (!cubeGroupRef.current) return;
78
- if (rotationSteps.current.length === 0 && !rotationStep) return;
79
- if (!rotationStep) {
80
- const step = rotationSteps.current.shift();
81
- if (!step) return;
82
- rotationStep = step;
83
- const cubes = getCubes(step.faceDirection);
84
- rotatingGroup = new Group();
85
- cubeGroupRef.current?.add(rotatingGroup);
86
- cubes.forEach((cube) => rotatingGroup.attach(cube));
87
- }
88
-
89
- const done = rotate(rotationStep, rotatingGroup, delta);
90
- if (done) {
91
- rotationStep = null;
92
- const children = [...rotatingGroup.children];
93
- children.forEach((child) => cubeGroupRef.current?.attach(child));
94
- cubeGroupRef.current?.remove(rotatingGroup);
95
- }
96
- };
97
- }, [cubeGroupRef, getCubes]);
98
- useFrame(frameCallback);
99
 
100
  return (
101
  <>
@@ -105,12 +31,12 @@ export const Rotator = forwardRef<RotatorRef, RotatorProps>(
105
  <RotationPanel
106
  direction="clockwise"
107
  facingDirection={facingDirection as FacingDirection}
108
- onClick={(s) => rotationSteps.current.push(s)}
109
  />
110
  <RotationPanel
111
  direction="counter-clockwise"
112
  facingDirection={facingDirection as FacingDirection}
113
- onClick={(s) => rotationSteps.current.push(s)}
114
  />
115
  </Fragment>
116
  ),
 
 
1
  import { FacingDirection, RotationStep } from "./consts";
2
  import { RotationPanel } from "./rotation-panel";
3
+ import { rotationController } from "./rotation-controller";
4
+ import { forwardRef, Fragment, useImperativeHandle } from "react";
 
 
 
 
 
 
5
  import { useFrame } from "@react-three/fiber";
6
 
7
  export type RotatorRef = {
 
14
 
15
  export const Rotator = forwardRef<RotatorRef, RotatorProps>(
16
  ({ cubeSpeed }: RotatorProps, ref) => {
17
+ rotationController.setCubeSpeed(cubeSpeed);
 
 
 
18
 
19
  useImperativeHandle(ref, () => ({
20
  rotate: (steps: Array<RotationStep>) =>
21
+ rotationController.addRotationStep(...steps),
22
  }));
23
 
24
+ useFrame(rotationController.frameCallback.bind(rotationController));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  return (
27
  <>
 
31
  <RotationPanel
32
  direction="clockwise"
33
  facingDirection={facingDirection as FacingDirection}
34
+ onClick={(s) => rotationController.addRotationStep(s)}
35
  />
36
  <RotationPanel
37
  direction="counter-clockwise"
38
  facingDirection={facingDirection as FacingDirection}
39
+ onClick={(s) => rotationController.addRotationStep(s)}
40
  />
41
  </Fragment>
42
  ),
src/components/rubiks-cube.tsx CHANGED
@@ -1,9 +1,9 @@
1
- import { useRef, forwardRef, useImperativeHandle } from "react";
2
  import { CubePiece } from "./cube-piece";
3
  import { Rotator, RotatorRef } from "./rotator";
4
- import { CubesProvider } from "@/contexts/cubes-context";
5
  import { Group } from "three";
6
  import { RotationStep } from "./consts";
 
7
 
8
  const CUBE_POSITIONS: Array<[number, number, number]> = [];
9
  for (let x = -0.5; x <= 0.5; x += 1) {
@@ -32,8 +32,13 @@ export const RubiksCube = forwardRef<RubiksCubeRef, RubiksCubeProps>(
32
  rotate: (steps: Array<RotationStep>) => rotatorRef.current?.rotate(steps),
33
  }));
34
 
 
 
 
 
 
35
  return (
36
- <CubesProvider cubeGroupRef={cubeGroupRef}>
37
  <group ref={cubeGroupRef}>
38
  {CUBE_POSITIONS.map((position) => (
39
  <CubePiece
@@ -44,7 +49,7 @@ export const RubiksCube = forwardRef<RubiksCubeRef, RubiksCubeProps>(
44
  ))}
45
  </group>
46
  <Rotator ref={rotatorRef} cubeSpeed={cubeSpeed} />
47
- </CubesProvider>
48
  );
49
  },
50
  );
 
1
+ import { useRef, forwardRef, useImperativeHandle, useEffect } from "react";
2
  import { CubePiece } from "./cube-piece";
3
  import { Rotator, RotatorRef } from "./rotator";
 
4
  import { Group } from "three";
5
  import { RotationStep } from "./consts";
6
+ import { rotationController } from "./rotation-controller";
7
 
8
  const CUBE_POSITIONS: Array<[number, number, number]> = [];
9
  for (let x = -0.5; x <= 0.5; x += 1) {
 
32
  rotate: (steps: Array<RotationStep>) => rotatorRef.current?.rotate(steps),
33
  }));
34
 
35
+ useEffect(() => {
36
+ if (cubeGroupRef.current)
37
+ rotationController.setCubeGroup(cubeGroupRef.current);
38
+ }, [cubeGroupRef]);
39
+
40
  return (
41
+ <>
42
  <group ref={cubeGroupRef}>
43
  {CUBE_POSITIONS.map((position) => (
44
  <CubePiece
 
49
  ))}
50
  </group>
51
  <Rotator ref={rotatorRef} cubeSpeed={cubeSpeed} />
52
+ </>
53
  );
54
  },
55
  );
src/contexts/cubes-context.tsx DELETED
@@ -1,63 +0,0 @@
1
- "use client";
2
-
3
- import { FacingDirection } from "@/components/consts";
4
- import { createContext, RefObject, useContext, useRef } from "react";
5
- import { Group, Mesh } from "three";
6
-
7
- export type CubeMeshRef = RefObject<Mesh | null>;
8
-
9
- type CubesContextType = {
10
- cubeGroupRef: RefObject<Group | null>;
11
- addCube: (cubeMeshRef: CubeMeshRef) => void;
12
- getCubes: (faceDirection: FacingDirection) => Mesh[];
13
- };
14
-
15
- const CubesContext = createContext<CubesContextType>({
16
- cubeGroupRef: { current: null },
17
- addCube: () => {},
18
- getCubes: () => [],
19
- });
20
-
21
- export const useCubesContext = () => useContext(CubesContext);
22
-
23
- export const CubesProvider = ({
24
- cubeGroupRef,
25
- children,
26
- }: {
27
- cubeGroupRef: RefObject<Group | null>;
28
- children: React.ReactNode;
29
- }) => {
30
- const cubes = useRef<CubeMeshRef[]>([]);
31
-
32
- const addCube = (cubeMeshRef: CubeMeshRef) => {
33
- if (!cubes.current.includes(cubeMeshRef)) {
34
- cubes.current.push(cubeMeshRef);
35
- }
36
- };
37
-
38
- const getCubes = (faceDirection: FacingDirection) => {
39
- const meshes = cubes.current
40
- .map((c) => c.current)
41
- .filter((m) => m !== null);
42
- switch (faceDirection) {
43
- case "front":
44
- return meshes.filter((m) => m.position.z > 0);
45
- case "back":
46
- return meshes.filter((m) => m.position.z < 0);
47
- case "left":
48
- return meshes.filter((m) => m.position.x < 0);
49
- case "right":
50
- return meshes.filter((m) => m.position.x > 0);
51
- case "top":
52
- return meshes.filter((m) => m.position.y > 0);
53
- case "bottom":
54
- return meshes.filter((m) => m.position.y < 0);
55
- }
56
- };
57
-
58
- return (
59
- <CubesContext.Provider value={{ cubeGroupRef, addCube, getCubes }}>
60
- {children}
61
- </CubesContext.Provider>
62
- );
63
- };