@houstonp/rubiks-cube 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +494 -63
  2. package/package.json +22 -12
  3. package/src/core/index.js +478 -0
  4. package/src/rubiksCube/index.js +3 -0
  5. package/src/rubiksCube/rubiksCubeController.js +111 -0
  6. package/src/rubiksCube3D/centerPiece.js +79 -0
  7. package/src/rubiksCube3D/cornerPiece.js +114 -0
  8. package/src/rubiksCube3D/cubeConfig.js +87 -0
  9. package/src/rubiksCube3D/cubeSettings.js +30 -0
  10. package/src/rubiksCube3D/edgePiece.js +51 -0
  11. package/src/rubiksCube3D/index.js +3 -0
  12. package/src/rubiksCube3D/rubiksCube3D.js +383 -0
  13. package/src/rubiksCube3D/sticker.js +38 -0
  14. package/src/state/index.js +4 -0
  15. package/src/state/rubiksCubeState.js +471 -0
  16. package/src/state/slice.js +236 -0
  17. package/src/state/stickerState.js +185 -0
  18. package/src/{cameraState.js → webComponent/cameraState.js} +17 -25
  19. package/src/webComponent/constants.js +67 -0
  20. package/src/{debouncer.js → webComponent/debouncer.js} +1 -1
  21. package/src/webComponent/index.js +7 -0
  22. package/src/webComponent/rubiksCubeElement.js +379 -0
  23. package/src/{settings.js → webComponent/settings.js} +47 -22
  24. package/tests/common.js +10 -0
  25. package/tests/core.test.js +56 -0
  26. package/tests/rubiksCube.solves.test.js +41 -0
  27. package/tests/rubiksCube3D.solves.test.js +185 -0
  28. package/tests/rubiksCubeState.solves.test.js +35 -0
  29. package/tests/setup.js +36 -0
  30. package/tests/testScrambles.js +194 -0
  31. package/types/core/index.d.ts +451 -0
  32. package/types/rubiksCube/index.d.ts +3 -0
  33. package/types/rubiksCube/rubiksCubeController.d.ts +62 -0
  34. package/types/rubiksCube3D/centerPiece.d.ts +27 -0
  35. package/types/rubiksCube3D/cornerPiece.d.ts +38 -0
  36. package/types/rubiksCube3D/cubeConfig.d.ts +32 -0
  37. package/types/rubiksCube3D/cubeSettings.d.ts +33 -0
  38. package/types/rubiksCube3D/edgePiece.d.ts +18 -0
  39. package/types/rubiksCube3D/index.d.ts +3 -0
  40. package/types/rubiksCube3D/rubiksCube3D.d.ts +120 -0
  41. package/types/rubiksCube3D/sticker.d.ts +18 -0
  42. package/types/state/index.d.ts +5 -0
  43. package/types/state/rubiksCubeState.d.ts +108 -0
  44. package/types/state/slice.d.ts +46 -0
  45. package/types/state/stickerState.d.ts +34 -0
  46. package/types/webComponent/cameraState.d.ts +22 -0
  47. package/types/webComponent/constants.d.ts +57 -0
  48. package/types/webComponent/index.d.ts +6 -0
  49. package/types/webComponent/rubiksCubeElement.d.ts +89 -0
  50. package/types/{settings.d.ts → webComponent/settings.d.ts} +9 -8
  51. package/src/core.js +0 -127
  52. package/src/cube/cube.js +0 -324
  53. package/src/cube/cubeRotation.js +0 -79
  54. package/src/cube/cubeSettings.js +0 -18
  55. package/src/cube/cubeState.js +0 -192
  56. package/src/cube/slice.js +0 -143
  57. package/src/index.js +0 -496
  58. package/src/schema.js +0 -22
  59. package/src/threejs/materials.js +0 -54
  60. package/src/threejs/pieces.js +0 -100
  61. package/src/threejs/stickers.js +0 -40
  62. package/types/cameraState.d.ts +0 -19
  63. package/types/core.d.ts +0 -125
  64. package/types/cube/cube.d.ts +0 -102
  65. package/types/cube/cubeRotation.d.ts +0 -33
  66. package/types/cube/cubeSettings.d.ts +0 -17
  67. package/types/cube/cubeState.d.ts +0 -16
  68. package/types/cube/slice.d.ts +0 -15
  69. package/types/index.d.ts +0 -65
  70. package/types/schema.d.ts +0 -11
  71. package/types/threejs/materials.d.ts +0 -21
  72. package/types/threejs/pieces.d.ts +0 -28
  73. package/types/threejs/stickers.d.ts +0 -6
  74. /package/src/{globals.ts → webComponent/globals.ts} +0 -0
  75. /package/types/{debouncer.d.ts → webComponent/debouncer.d.ts} +0 -0
  76. /package/types/{globals.d.ts → webComponent/globals.d.ts} +0 -0
@@ -0,0 +1,79 @@
1
+ // @ts-check
2
+ import { BoxGeometry, ExtrudeGeometry, Mesh, MeshBasicMaterial, MeshStandardMaterial, Object3D, PlaneGeometry, SRGBColorSpace, TextureLoader } from 'three';
3
+ import { SVGLoader } from 'three/examples/jsm/Addons.js';
4
+ import { Sticker } from './sticker';
5
+ /** @import {Vector3Like} from 'three' */
6
+
7
+ /** @typedef {{ positon: Vector3Like, rotation: Vector3Like }} CenterPieceUserData */
8
+
9
+ export class CenterPiece extends Object3D {
10
+ constructor() {
11
+ super();
12
+ const boxGeom = new BoxGeometry(1, 1, 1);
13
+ const boxMesh = new Mesh(boxGeom, new MeshBasicMaterial({ color: 'black' }));
14
+ this.add(boxMesh);
15
+ /** @type {CenterPieceUserData} */
16
+ this.userData = { position: { x: 0, y: 0, z: 0 }, rotation: { x: 0, y: 0, z: 0 } };
17
+
18
+ this.frontSticker = new CenterSticker();
19
+ this.frontSticker.position.set(0, 0, 0.5);
20
+ this.frontSticker.rotation.set(0, 0, 0);
21
+ this.add(this.frontSticker);
22
+
23
+ /** @type {Mesh<PlaneGeometry, MeshStandardMaterial> | null} */
24
+ this.logo = null;
25
+ }
26
+
27
+ get stickers() {
28
+ return [this.frontSticker];
29
+ }
30
+
31
+ /**
32
+ * @param {string} logoPath
33
+ */
34
+ addLogo(logoPath) {
35
+ this.removeLogo();
36
+ const material = new MeshStandardMaterial({
37
+ transparent: true,
38
+ color: 'white',
39
+ metalness: 0,
40
+ roughness: 0.4,
41
+ });
42
+ const mesh = new Mesh(new PlaneGeometry(0.9, 0.9), material);
43
+ mesh.position.set(0, 0, 0.54);
44
+ this.logo = mesh;
45
+ this.add(mesh);
46
+ const texture = new TextureLoader().load(logoPath, (texture) => {
47
+ texture.colorSpace = SRGBColorSpace;
48
+ material.map = texture;
49
+ material.needsUpdate = true;
50
+ texture.anisotropy = 16;
51
+ });
52
+ }
53
+
54
+ removeLogo() {
55
+ if (!this.logo) return;
56
+ this.remove(this.logo);
57
+ this.logo.material.map?.dispose();
58
+ this.logo.material.dispose();
59
+ this.logo = null;
60
+ }
61
+ }
62
+
63
+ const loader = new SVGLoader();
64
+ const centerSVG = loader.parse(`
65
+ <svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
66
+ <path d="M 120 0 L 380 0 C 450 0 500 50 500 120 L 500 380 C 500 450 450 500 380 500 L 120 500 C 50 500 0 450 0 380 L 0 120 C 0 50 50 0 120 0 Z"></path>
67
+ </svg>
68
+ `);
69
+ const centerGeometry = new ExtrudeGeometry(SVGLoader.createShapes(centerSVG.paths[0])[0], {
70
+ depth: 15,
71
+ })
72
+ .scale(0.002, 0.002, 0.002)
73
+ .translate(-0.5, -0.5, 0);
74
+
75
+ export class CenterSticker extends Sticker {
76
+ constructor() {
77
+ super(centerGeometry);
78
+ }
79
+ }
@@ -0,0 +1,114 @@
1
+ /// @ts-check
2
+ import {
3
+ BoxGeometry,
4
+ ExtrudeGeometry,
5
+ Material,
6
+ Mesh,
7
+ MeshBasicMaterial,
8
+ MeshStandardMaterial,
9
+ Object3D,
10
+ PlaneGeometry,
11
+ SRGBColorSpace,
12
+ TextureLoader,
13
+ } from 'three';
14
+ import { Sticker } from './sticker';
15
+ import { SVGLoader } from 'three/examples/jsm/Addons.js';
16
+ import { Faces } from '../core';
17
+ /** @import {Vector3Like} from 'three' */
18
+ /** @import {Face} from "../core" */
19
+
20
+ /** @typedef {{ positon: Vector3Like, rotation: Vector3Like }} CornerPieceUserData */
21
+ /**
22
+ * @param {Material} frontMaterial
23
+ * @param {Material} rightMaterial
24
+ * @param {Material} topMaterial
25
+ * @param {Material} coreMaterial
26
+ * @returns {Group}
27
+ */
28
+ export class CornerPiece extends Object3D {
29
+ constructor() {
30
+ super();
31
+ const boxGeom = new BoxGeometry(1, 1, 1);
32
+ const boxMesh = new Mesh(boxGeom, new MeshBasicMaterial({ color: 'black' }));
33
+ this.add(boxMesh);
34
+ /** @type {CornerPieceUserData} */
35
+ this.userData = { position: { x: 0, y: 0, z: 0 }, rotation: { x: 0, y: 0, z: 0 } };
36
+
37
+ this.frontSticker = new CornerSticker();
38
+ this.frontSticker.position.set(0, 0, 0.5);
39
+ this.frontSticker.rotation.set(0, 0, 0);
40
+ this.add(this.frontSticker);
41
+
42
+ this.rightSticker = new CornerSticker();
43
+ this.rightSticker.position.set(0.5, 0, 0);
44
+ this.rightSticker.rotation.set(Math.PI / 2, Math.PI / 2, 0);
45
+ this.add(this.rightSticker);
46
+
47
+ this.topSticker = new CornerSticker();
48
+ this.topSticker.position.set(0, 0.5, 0);
49
+ this.topSticker.rotation.set(-Math.PI / 2, 0, -Math.PI / 2);
50
+ this.add(this.topSticker);
51
+ }
52
+
53
+ get stickers() {
54
+ return [this.frontSticker, this.rightSticker, this.topSticker];
55
+ }
56
+
57
+ /**
58
+ * @param {Face} face
59
+ * @param {string} logoPath
60
+ */
61
+ addLogo(face, logoPath) {
62
+ this.removeLogo();
63
+ const material = new MeshStandardMaterial({
64
+ transparent: true,
65
+ color: 'white',
66
+ metalness: 0,
67
+ roughness: 0.4,
68
+ });
69
+ const mesh = new Mesh(new PlaneGeometry(0.9, 0.9), material);
70
+ if (face === Faces.U) {
71
+ mesh.position.set(0, 0.54, 0);
72
+ mesh.rotation.set(-Math.PI / 2, 0, -Math.PI / 2);
73
+ } else if (face === Faces.F) {
74
+ mesh.position.set(0, 0, 0.54);
75
+ } else if (face === Faces.R) {
76
+ mesh.position.set(0.54, 0, 0);
77
+ mesh.rotation.set(Math.PI / 2, Math.PI / 2, 0);
78
+ }
79
+ this.logo = mesh;
80
+ this.add(mesh);
81
+ const texture = new TextureLoader().load(logoPath, (texture) => {
82
+ texture.colorSpace = SRGBColorSpace;
83
+ material.map = texture;
84
+ material.needsUpdate = true;
85
+ texture.anisotropy = 16;
86
+ });
87
+ }
88
+
89
+ removeLogo() {
90
+ if (!this.logo) return;
91
+ this.remove(this.logo);
92
+ this.logo.material.map?.dispose();
93
+ this.logo.material.dispose();
94
+ this.logo = null;
95
+ }
96
+ }
97
+
98
+ const loader = new SVGLoader();
99
+ const cornerSVG = loader.parse(`
100
+ <svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" xmlns:bx="https://boxy-svg.com">
101
+ <path d="M 25 0 H 500 V 500 H 0 V 25 A 25 25 0 0 1 25 0 Z" bx:shape="rect 0 0 500 500 25 0 0 0 1@a864c1ee"/>
102
+ </svg>
103
+ `);
104
+ const cornerGeometry = new ExtrudeGeometry(SVGLoader.createShapes(cornerSVG.paths[0])[0], {
105
+ depth: 15,
106
+ })
107
+ .scale(0.002, 0.002, 0.002)
108
+ .translate(-0.5, -0.5, 0);
109
+
110
+ export class CornerSticker extends Sticker {
111
+ constructor() {
112
+ super(cornerGeometry);
113
+ }
114
+ }
@@ -0,0 +1,87 @@
1
+ // @ts-check
2
+ import { CubeTypes, Faces } from '../core';
3
+ /** @import {CubeType, Face} from '../core' */
4
+ /** @import {ColorRepresentation} from 'three' */
5
+
6
+ /**
7
+ * @typedef CubeConfig
8
+ * @property {number[]} layers
9
+ * @property {number} pieceSize
10
+ * @property {number} coreSize
11
+ * @property {number} outerLayerMultiplier
12
+ */
13
+
14
+ /**
15
+ * @param {CubeType} cubeType
16
+ * @return {CubeConfig}
17
+ */
18
+ export function getCubeConfig(cubeType) {
19
+ switch (cubeType) {
20
+ case CubeTypes.Two:
21
+ return {
22
+ layers: [-1, 1],
23
+ pieceSize: 2,
24
+ coreSize: 1.7,
25
+ outerLayerMultiplier: 1,
26
+ };
27
+ break;
28
+ case CubeTypes.Three:
29
+ return {
30
+ layers: [-1, 0, 1],
31
+ pieceSize: 1,
32
+ coreSize: 1.32,
33
+ outerLayerMultiplier: 1,
34
+ };
35
+ case CubeTypes.Four:
36
+ return {
37
+ layers: [-1, -1 / 3, 1 / 3, 1],
38
+ pieceSize: 2 / 3,
39
+ coreSize: 1.25,
40
+ outerLayerMultiplier: 1.1,
41
+ };
42
+ case CubeTypes.Five:
43
+ return {
44
+ layers: [-1, -1 / 2, 0, 1 / 2, 1],
45
+ pieceSize: 1 / 2,
46
+ coreSize: 1.2,
47
+ outerLayerMultiplier: 1.2,
48
+ };
49
+ case CubeTypes.Six:
50
+ return {
51
+ layers: [-1, -3 / 5, -1 / 5, 1 / 5, 3 / 5, 1],
52
+ pieceSize: 2 / 5,
53
+ coreSize: 1.18,
54
+ outerLayerMultiplier: 1.3,
55
+ };
56
+ case CubeTypes.Seven:
57
+ return {
58
+ layers: [-1, -2 / 3, -1 / 3, 0, 1 / 3, 2 / 3, 1],
59
+ pieceSize: 1 / 3,
60
+ coreSize: 1.16,
61
+ outerLayerMultiplier: 1.35,
62
+ };
63
+ default:
64
+ throw new Error(`Unsupported cube type: ${cubeType}`);
65
+ }
66
+ }
67
+
68
+ export const FaceColors = {
69
+ [Faces.B]: 'blue',
70
+ [Faces.D]: 'yellow',
71
+ [Faces.F]: '#2cbf13',
72
+ [Faces.L]: '#fc9a05',
73
+ [Faces.R]: 'red',
74
+ [Faces.U]: 'white',
75
+ };
76
+
77
+ /**
78
+ * @param {ColorRepresentation} color
79
+ * @return {Face}
80
+ * */
81
+ export const ColorToFace = (color) => {
82
+ const face = Object.values(Faces).find((face) => FaceColors[face] === color);
83
+ if (!face) {
84
+ throw new Error(`Invalid color: ${color}`);
85
+ }
86
+ return face;
87
+ };
@@ -0,0 +1,30 @@
1
+ // @ts-check
2
+ import { CubeTypes } from '../core';
3
+ /** @import {CubeType} from '../core' */
4
+
5
+ /**
6
+ * @typedef RubiksCube3DSettingsOptions
7
+ * @property {CubeType} [cubeType]
8
+ * @property {number} [pieceGap]
9
+ * @property {number} [animationSpeedMs]
10
+ * @property {gsap.EaseString | gsap.EaseFunction} [animationStyle]
11
+ * @property {string | null} [logo]
12
+ */
13
+
14
+ export default class RubiksCube3DSettings {
15
+ /**
16
+ * @param {RubiksCube3DSettingsOptions} [options]
17
+ */
18
+ constructor(options = {}) {
19
+ /** @type {CubeType} */
20
+ this.cubeType = options.cubeType ?? CubeTypes.Three;
21
+ /** @type {number} */
22
+ this.pieceGap = options.pieceGap ?? 1.04;
23
+ /** @type {number} */
24
+ this.animationSpeedMs = options.animationSpeedMs ?? 150;
25
+ /** @type {gsap.EaseString | gsap.EaseFunction} */
26
+ this.animationStyle = options.animationStyle ?? 'sine';
27
+ /** @type {string | null} */
28
+ this.logo = options.logo ?? null;
29
+ }
30
+ }
@@ -0,0 +1,51 @@
1
+ // @ts-check
2
+ import { BoxGeometry, ExtrudeGeometry, Mesh, MeshBasicMaterial, Object3D } from 'three';
3
+ import { SVGLoader } from 'three/examples/jsm/Addons.js';
4
+ import { Sticker } from './sticker';
5
+ /** @import {Vector3Like} from 'three' */
6
+
7
+ /** @typedef {{ positon: Vector3Like, rotation: Vector3Like }} EdgePieceUserData*/
8
+
9
+ export class EdgePiece extends Object3D {
10
+ constructor() {
11
+ super();
12
+ const boxGeom = new BoxGeometry(1, 1, 1);
13
+ const boxMesh = new Mesh(boxGeom, new MeshBasicMaterial({ color: 'black' }));
14
+ this.add(boxMesh);
15
+
16
+ /** @type {EdgePieceUserData} */
17
+ this.userData = { position: { x: 0, y: 0, z: 0 }, rotation: { x: 0, y: 0, z: 0 } };
18
+
19
+ this.frontSticker = new EdgeSticker();
20
+ this.frontSticker.position.set(0, 0, 0.5);
21
+ this.frontSticker.rotation.set(0, 0, 0);
22
+ this.add(this.frontSticker);
23
+
24
+ this.topSticker = new EdgeSticker();
25
+ this.topSticker.position.set(0, 0.5, 0);
26
+ this.topSticker.rotation.set(-Math.PI / 2, 0, Math.PI);
27
+ this.add(this.topSticker);
28
+ }
29
+
30
+ get stickers() {
31
+ return [this.frontSticker, this.topSticker];
32
+ }
33
+ }
34
+
35
+ const loader = new SVGLoader();
36
+ const edgeSVG = loader.parse(`
37
+ <svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
38
+ <path d="M 150 0 L 350 0 C 450 0 500 50 500 120 L 500 500 L 0 500 L 0 120 C 0 50 50 0 150 0 Z"></path>
39
+ </svg>
40
+ `);
41
+ const edgeGeometry = new ExtrudeGeometry(SVGLoader.createShapes(edgeSVG.paths[0])[0], {
42
+ depth: 15,
43
+ })
44
+ .scale(0.002, 0.002, 0.002)
45
+ .translate(-0.5, -0.5, 0);
46
+
47
+ export class EdgeSticker extends Sticker {
48
+ constructor() {
49
+ super(edgeGeometry);
50
+ }
51
+ }
@@ -0,0 +1,3 @@
1
+ import RubiksCube3D from './rubiksCube3D';
2
+ import RubiksCube3DSettings from './cubeSettings';
3
+ export { RubiksCube3D, RubiksCube3DSettings };