File size: 2,461 Bytes
1afe868
 
 
 
7994c21
1afe868
 
a6e23da
 
ec43676
 
 
a6e23da
 
 
 
 
7cefe36
9d0c4b5
7994c21
7cefe36
 
a6e23da
aa74807
 
a6e23da
 
1afe868
 
 
7994c21
1afe868
 
 
7994c21
 
 
 
 
7d70895
 
 
 
 
 
 
 
52b5f8d
7994c21
 
1afe868
 
 
52b5f8d
 
 
1afe868
 
 
 
 
7994c21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1afe868
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
import { Group } from 'three';

import { RotationStep } from './consts';
import { CubePiece, CubePieceRef } from './cube-piece';
import { rotationController } from './rotation-controller';
import { Rotator, RotatorRef } from './rotator';

const CUBE_POSITIONS: Array<[number, number, number]> = [];
for (let x = -0.5; x <= 0.5; x += 1) {
  for (let y = -0.5; y <= 0.5; y += 1) {
    for (let z = -0.5; z <= 0.5; z += 1) {
      CUBE_POSITIONS.push([x, y, z]);
    }
  }
}

export type RubiksCubeRef = {
  rotate: (steps: Array<RotationStep>) => void;
  reset: () => void;
};

type RubiksCubeProps = {
  cubeRoughness: number;
  cubeSpeed: number;
};

export const RubiksCube = forwardRef<RubiksCubeRef, RubiksCubeProps>(({ cubeRoughness, cubeSpeed }, ref) => {
  const cubeGroupRef = useRef<Group | null>(null);
  const rotatorRef = useRef<RotatorRef | null>(null);
  const cubePieceRefs = useRef<Map<string, CubePieceRef>>(new Map());

  useImperativeHandle(ref, () => ({
    rotate: (steps: Array<RotationStep>) => rotatorRef.current?.rotate(steps),
    reset: () => {
      rotationController.stopRotation(() => {
        cubePieceRefs.current.forEach((cubePieceRef) => {
          cubePieceRef.resetPosition();
        });
        rotationController.setState([
          [0, 0, 0, 0],
          [1, 1, 1, 1],
          [2, 2, 2, 2],
          [3, 3, 3, 3],
          [4, 4, 4, 4],
          [5, 5, 5, 5],
        ]);
        rotationController.initializeFaces();
      });
    },
  }));

  useEffect(() => {
    if (!cubeGroupRef.current) return;
    rotationController.setCubeGroup(cubeGroupRef.current);
    rotationController.initializeFaces();
  }, [cubeGroupRef]);

  return (
    <>
      <group ref={cubeGroupRef}>
        {CUBE_POSITIONS.map((position) => {
          const positionKey = position.join(',');
          return (
            <CubePiece
              key={positionKey}
              ref={(ref) => {
                if (ref) {
                  cubePieceRefs.current.set(positionKey, ref);
                } else {
                  cubePieceRefs.current.delete(positionKey);
                }
              }}
              initialPosition={position}
              roughness={cubeRoughness}
            />
          );
        })}
      </group>
      <Rotator ref={rotatorRef} cubeSpeed={cubeSpeed} />
    </>
  );
});

RubiksCube.displayName = 'RubiksCube';