@rednaxela101/cubing 0.63.7 → 0.63.9
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.
- package/README.md +17 -11
- package/dist/bin/chunks/{chunk-6YZ4AUWL.js → chunk-F63RAIBR.js} +2 -2
- package/dist/bin/order.js +1 -1
- package/dist/bin/puzzle-geometry-bin.js +1 -1
- package/dist/bin/scramble.js +1 -1
- package/dist/bin/svg.js +1 -1
- package/dist/lib/cubing/chunks/{chunk-GP66KY36.js → chunk-F765ZFNM.js} +2 -2
- package/dist/lib/cubing/chunks/{twisty-dynamic-3d-XQOH6E2O.js → twisty-dynamic-3d-MF76FSDA.js} +2010 -266
- package/dist/lib/cubing/chunks/twisty-dynamic-3d-MF76FSDA.js.map +7 -0
- package/dist/lib/cubing/twisty/index.js +3 -2
- package/dist/lib/cubing/twisty/index.js.map +2 -2
- package/package.json +1 -1
- package/dist/lib/cubing/chunks/twisty-dynamic-3d-XQOH6E2O.js.map +0 -7
- /package/dist/bin/chunks/{chunk-6YZ4AUWL.js.map → chunk-F63RAIBR.js.map} +0 -0
- /package/dist/lib/cubing/chunks/{chunk-GP66KY36.js.map → chunk-F765ZFNM.js.map} +0 -0
package/dist/lib/cubing/chunks/{twisty-dynamic-3d-XQOH6E2O.js → twisty-dynamic-3d-MF76FSDA.js}
RENAMED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
bulk3DCode,
|
|
4
4
|
haveStartedSharingRenderers,
|
|
5
5
|
hintFaceletStyles
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-F765ZFNM.js";
|
|
7
7
|
import "./chunk-3ODTF4HE.js";
|
|
8
8
|
import {
|
|
9
9
|
cube3x3x3,
|
|
@@ -16,18 +16,17 @@ import {
|
|
|
16
16
|
|
|
17
17
|
// src/cubing/twisty/views/3D/puzzles/Cube3D.ts
|
|
18
18
|
import { BackSide, DoubleSide, FrontSide } from "three/src/constants.js";
|
|
19
|
-
import { BufferAttribute } from "three/src/core/BufferAttribute.js";
|
|
20
|
-
import { BufferGeometry } from "three/src/core/BufferGeometry.js";
|
|
19
|
+
import { BufferAttribute as BufferAttribute2 } from "three/src/core/BufferAttribute.js";
|
|
20
|
+
import { BufferGeometry as BufferGeometry2 } from "three/src/core/BufferGeometry.js";
|
|
21
21
|
import { Object3D } from "three/src/core/Object3D.js";
|
|
22
22
|
import { BoxGeometry } from "three/src/geometries/BoxGeometry.js";
|
|
23
23
|
import { TextureLoader } from "three/src/loaders/TextureLoader.js";
|
|
24
|
-
import { MeshBasicMaterial } from "three/src/materials/MeshBasicMaterial.js";
|
|
25
|
-
import { Color } from "three/src/math/Color.js";
|
|
26
|
-
import { Euler } from "three/src/math/Euler.js";
|
|
24
|
+
import { MeshBasicMaterial as MeshBasicMaterial2 } from "three/src/materials/MeshBasicMaterial.js";
|
|
25
|
+
import { Color as Color2 } from "three/src/math/Color.js";
|
|
27
26
|
import { Matrix4 } from "three/src/math/Matrix4.js";
|
|
28
27
|
import { Quaternion } from "three/src/math/Quaternion.js";
|
|
29
28
|
import { Vector2 } from "three/src/math/Vector2.js";
|
|
30
|
-
import { Vector3 } from "three/src/math/Vector3.js";
|
|
29
|
+
import { Vector3 as Vector33 } from "three/src/math/Vector3.js";
|
|
31
30
|
import { Group } from "three/src/objects/Group.js";
|
|
32
31
|
import { Mesh } from "three/src/objects/Mesh.js";
|
|
33
32
|
|
|
@@ -36,63 +35,316 @@ function smootherStep(x) {
|
|
|
36
35
|
return x * x * x * (10 - x * (15 - 6 * x));
|
|
37
36
|
}
|
|
38
37
|
|
|
38
|
+
// src/cubing/twisty/views/3D/puzzles/BeveledCubieGeometry.ts
|
|
39
|
+
import { BufferAttribute } from "three/src/core/BufferAttribute.js";
|
|
40
|
+
import { BufferGeometry } from "three/src/core/BufferGeometry.js";
|
|
41
|
+
import { Vector3 } from "three/src/math/Vector3.js";
|
|
42
|
+
function clamp(value, bound) {
|
|
43
|
+
return Math.min(Math.max(value, -bound), bound);
|
|
44
|
+
}
|
|
45
|
+
function component(vector, axis) {
|
|
46
|
+
return axis === 0 ? vector.x : axis === 1 ? vector.y : vector.z;
|
|
47
|
+
}
|
|
48
|
+
function setComponent(vector, axis, value) {
|
|
49
|
+
if (axis === 0) {
|
|
50
|
+
vector.x = value;
|
|
51
|
+
} else if (axis === 1) {
|
|
52
|
+
vector.y = value;
|
|
53
|
+
} else {
|
|
54
|
+
vector.z = value;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function beveledCubieGeometry(faceNormals, outerFaceIndices, halfSize, outerAxisRadius, innerEdgeRadius, innerCornerRadius, cornerSharpness, segments) {
|
|
58
|
+
const allRadii = [outerAxisRadius, innerEdgeRadius, innerCornerRadius];
|
|
59
|
+
const maxRadius = Math.max(...allRadii);
|
|
60
|
+
if (!(Math.min(...allRadii) > 0 && maxRadius < halfSize)) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
"Every radius must be positive and smaller than the half-size."
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (cornerSharpness <= 0 || segments < 1) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
"The corner sharpness must be positive, with at least one segment."
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const outward = [
|
|
71
|
+
[0, 0],
|
|
72
|
+
[0, 0],
|
|
73
|
+
[0, 0]
|
|
74
|
+
];
|
|
75
|
+
for (let faceIdx = 0; faceIdx < faceNormals.length; faceIdx++) {
|
|
76
|
+
const normal2 = faceNormals[faceIdx];
|
|
77
|
+
const axis = Math.abs(normal2.x) > 0.5 ? 0 : Math.abs(normal2.y) > 0.5 ? 1 : 2;
|
|
78
|
+
outward[axis][component(normal2, axis) > 0 ? 1 : 0] = outerFaceIndices.includes(faceIdx) ? 1 : 0;
|
|
79
|
+
}
|
|
80
|
+
const inner = halfSize - maxRadius;
|
|
81
|
+
const samples = [];
|
|
82
|
+
for (let i = 0; i <= segments; i++) {
|
|
83
|
+
samples.push(-halfSize + maxRadius * i / segments);
|
|
84
|
+
}
|
|
85
|
+
for (let i = 0; i <= segments; i++) {
|
|
86
|
+
samples.push(inner + maxRadius * i / segments);
|
|
87
|
+
}
|
|
88
|
+
const gridSize = samples.length;
|
|
89
|
+
const positions = [];
|
|
90
|
+
const indices = [];
|
|
91
|
+
const geometry = new BufferGeometry();
|
|
92
|
+
const point = new Vector3();
|
|
93
|
+
const core = new Vector3();
|
|
94
|
+
const offset = new Vector3();
|
|
95
|
+
const u = new Vector3();
|
|
96
|
+
const v = new Vector3();
|
|
97
|
+
const radii = new Vector3();
|
|
98
|
+
const outerness = [0, 0, 0];
|
|
99
|
+
const radiiAt = (p, out) => {
|
|
100
|
+
const towardCorner = (Math.abs(p.x) * Math.abs(p.y) * Math.abs(p.z) / (halfSize * halfSize * halfSize)) ** cornerSharpness;
|
|
101
|
+
let outwardFaceCount = 0;
|
|
102
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
103
|
+
const towardPositive = (component(p, axis) / halfSize + 1) / 2;
|
|
104
|
+
outerness[axis] = towardPositive * outward[axis][1] + (1 - towardPositive) * outward[axis][0];
|
|
105
|
+
outwardFaceCount += outerness[axis];
|
|
106
|
+
}
|
|
107
|
+
const interiorCorner = Math.min(Math.max(2 - outwardFaceCount, 0), 1);
|
|
108
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
109
|
+
const alongEdge = innerEdgeRadius + (outerAxisRadius - innerEdgeRadius) * outerness[axis];
|
|
110
|
+
const atCorner = innerCornerRadius + (outerAxisRadius - innerCornerRadius) * outerness[axis];
|
|
111
|
+
setComponent(
|
|
112
|
+
out,
|
|
113
|
+
axis,
|
|
114
|
+
alongEdge + (atCorner - alongEdge) * towardCorner * interiorCorner
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
for (let faceIdx = 0; faceIdx < faceNormals.length; faceIdx++) {
|
|
119
|
+
const normal2 = faceNormals[faceIdx];
|
|
120
|
+
u.set(normal2.y, normal2.z, normal2.x);
|
|
121
|
+
v.crossVectors(normal2, u);
|
|
122
|
+
const vertexStart = positions.length / 3;
|
|
123
|
+
for (let i = 0; i < gridSize; i++) {
|
|
124
|
+
for (let j = 0; j < gridSize; j++) {
|
|
125
|
+
point.copy(u).multiplyScalar(samples[i]).addScaledVector(v, samples[j]).addScaledVector(normal2, halfSize);
|
|
126
|
+
radiiAt(point, radii);
|
|
127
|
+
core.set(
|
|
128
|
+
clamp(point.x, halfSize - radii.x),
|
|
129
|
+
clamp(point.y, halfSize - radii.y),
|
|
130
|
+
clamp(point.z, halfSize - radii.z)
|
|
131
|
+
);
|
|
132
|
+
offset.set(
|
|
133
|
+
(point.x - core.x) / radii.x,
|
|
134
|
+
(point.y - core.y) / radii.y,
|
|
135
|
+
(point.z - core.z) / radii.z
|
|
136
|
+
).normalize();
|
|
137
|
+
positions.push(
|
|
138
|
+
core.x + offset.x * radii.x,
|
|
139
|
+
core.y + offset.y * radii.y,
|
|
140
|
+
core.z + offset.z * radii.z
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const indexStart = indices.length;
|
|
145
|
+
for (let i = 0; i < gridSize - 1; i++) {
|
|
146
|
+
for (let j = 0; j < gridSize - 1; j++) {
|
|
147
|
+
const a = vertexStart + i * gridSize + j;
|
|
148
|
+
const b = a + gridSize;
|
|
149
|
+
indices.push(a, b, b + 1, a, b + 1, a + 1);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
geometry.addGroup(indexStart, indices.length - indexStart, faceIdx);
|
|
153
|
+
}
|
|
154
|
+
geometry.setAttribute(
|
|
155
|
+
"position",
|
|
156
|
+
new BufferAttribute(new Float32Array(positions), 3)
|
|
157
|
+
);
|
|
158
|
+
geometry.setIndex(indices);
|
|
159
|
+
return geometry;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/cubing/twisty/views/3D/puzzles/CubieStyle.ts
|
|
163
|
+
import { MeshBasicMaterial } from "three/src/materials/MeshBasicMaterial.js";
|
|
164
|
+
import { Color } from "three/src/math/Color.js";
|
|
165
|
+
import { Euler } from "three/src/math/Euler.js";
|
|
166
|
+
import { Vector3 as Vector32 } from "three/src/math/Vector3.js";
|
|
167
|
+
var cubeFaceStyles = [
|
|
168
|
+
{
|
|
169
|
+
vector: new Vector32(0, 1, 0),
|
|
170
|
+
fromZ: new Euler(-TAU / 4, 0, 0),
|
|
171
|
+
color: 16777215,
|
|
172
|
+
dimColor: 14540253,
|
|
173
|
+
hintColor: 16777215,
|
|
174
|
+
hintDimColor: 14540253,
|
|
175
|
+
hintOpacityScale: 1.25
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
vector: new Vector32(-1, 0, 0),
|
|
179
|
+
fromZ: new Euler(0, -TAU / 4, 0),
|
|
180
|
+
color: 16750848,
|
|
181
|
+
dimColor: 8934656,
|
|
182
|
+
hintColor: 16750848,
|
|
183
|
+
hintDimColor: 8930304,
|
|
184
|
+
hintOpacityScale: 1
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
vector: new Vector32(0, 0, 1),
|
|
188
|
+
fromZ: new Euler(0, 0, 0),
|
|
189
|
+
color: 65280,
|
|
190
|
+
dimColor: 34816,
|
|
191
|
+
hintColor: 65280,
|
|
192
|
+
hintDimColor: 39168,
|
|
193
|
+
hintOpacityScale: 1
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
vector: new Vector32(1, 0, 0),
|
|
197
|
+
fromZ: new Euler(0, TAU / 4, 0),
|
|
198
|
+
color: 16711680,
|
|
199
|
+
dimColor: 6684672,
|
|
200
|
+
hintColor: 16711680,
|
|
201
|
+
hintDimColor: 6684672,
|
|
202
|
+
hintOpacityScale: 1
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
vector: new Vector32(0, 0, -1),
|
|
206
|
+
fromZ: new Euler(0, TAU / 2, 0),
|
|
207
|
+
color: 2254591,
|
|
208
|
+
dimColor: 1127304,
|
|
209
|
+
hintColor: 2254591,
|
|
210
|
+
hintDimColor: 6246,
|
|
211
|
+
hintOpacityScale: 0.75
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
vector: new Vector32(0, -1, 0),
|
|
215
|
+
fromZ: new Euler(TAU / 4, 0, 0),
|
|
216
|
+
color: 16776960,
|
|
217
|
+
dimColor: 8947712,
|
|
218
|
+
hintColor: 16776960,
|
|
219
|
+
hintDimColor: 14540032,
|
|
220
|
+
hintOpacityScale: 1.25
|
|
221
|
+
}
|
|
222
|
+
];
|
|
223
|
+
var cubieBodyDimensions = {
|
|
224
|
+
/**
|
|
225
|
+
* Half-width of a `stickerless` cubie body. Deliberately more than the 0.5
|
|
226
|
+
* that would make pieces exactly fill their slot: oversized pieces press into
|
|
227
|
+
* each other, which buries most of each rounded edge inside its neighbor and
|
|
228
|
+
* keeps the dividing lines thin.
|
|
229
|
+
*/
|
|
230
|
+
halfWidth: 0.54,
|
|
231
|
+
/**
|
|
232
|
+
* Roll at the rim of a facelet, along the axis the facelet faces — so also
|
|
233
|
+
* the rounding of the puzzle's own outer edges and corners. Effectively zero,
|
|
234
|
+
* which leaves those edges sharp and the plates dead flat.
|
|
235
|
+
*
|
|
236
|
+
* Not exactly zero: the construction lifts each face off the core box along
|
|
237
|
+
* this axis, so a true zero leaves nothing to normalize in the middle of a
|
|
238
|
+
* face. A few thousandths is below a pixel at any sane size.
|
|
239
|
+
*/
|
|
240
|
+
outerAxisRadius: 4e-3,
|
|
241
|
+
/**
|
|
242
|
+
* Rounding along an axis pointing at a neighboring piece, in the middle of an
|
|
243
|
+
* edge. Sets how wide the dividing line between two pieces reads.
|
|
244
|
+
*/
|
|
245
|
+
innerEdgeRadius: 0.07,
|
|
246
|
+
/**
|
|
247
|
+
* The same axis at a corner. This one rounds a facelet's corners within its
|
|
248
|
+
* own plane, so it is what turns a center into a disc, and it costs no
|
|
249
|
+
* thickness because the roll stays `outerAxisRadius` deep.
|
|
250
|
+
*/
|
|
251
|
+
innerCornerRadius: 0.34,
|
|
252
|
+
/** How tightly the corner rounding is pulled in toward the corners. */
|
|
253
|
+
cornerSharpness: 1.3,
|
|
254
|
+
/**
|
|
255
|
+
* Everything above, shrunk about each piece's own center. Scaling rather than
|
|
256
|
+
* trimming the half-width leaves every proportion of the piece untouched and
|
|
257
|
+
* only opens a gap against its neighbors, so the pieces read as separate with
|
|
258
|
+
* a thin line of the puzzle's interior showing between them.
|
|
259
|
+
*/
|
|
260
|
+
pieceScale: 0.95,
|
|
261
|
+
roundingSegments: 3
|
|
262
|
+
};
|
|
263
|
+
function cubieBodyHalfExtent(layers) {
|
|
264
|
+
return layers / 2 - 0.5 + cubieBodyDimensions.halfWidth * cubieBodyDimensions.pieceScale;
|
|
265
|
+
}
|
|
266
|
+
function newBodyMaterial(color) {
|
|
267
|
+
return new MeshBasicMaterial({
|
|
268
|
+
color: typeof color === "number" ? new Color(color).convertLinearToSRGB() : color
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function newVertexColorBodyMaterial() {
|
|
272
|
+
const material = newBodyMaterial(16777215);
|
|
273
|
+
material.vertexColors = true;
|
|
274
|
+
return material;
|
|
275
|
+
}
|
|
276
|
+
var bodyMaskColors = {
|
|
277
|
+
/** The plastic that shows through the grooves between pieces. */
|
|
278
|
+
internal: 921102,
|
|
279
|
+
ignored: 6710886,
|
|
280
|
+
oriented: 4513228,
|
|
281
|
+
experimentalOriented2: 16776618,
|
|
282
|
+
mystery: 15911883
|
|
283
|
+
};
|
|
284
|
+
var hintMaskStyles = {
|
|
285
|
+
ignored: { color: 13421772, opacity: 0.75 },
|
|
286
|
+
oriented: { color: 4513228, opacity: 0.5 },
|
|
287
|
+
experimentalOriented2: { color: 16775545, opacity: 0.5 },
|
|
288
|
+
mystery: { color: 15911883, opacity: 0.5 }
|
|
289
|
+
};
|
|
290
|
+
|
|
39
291
|
// src/cubing/twisty/views/3D/puzzles/Cube3D.ts
|
|
40
292
|
var svgLoader = new TextureLoader();
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
293
|
+
function newHintMaterial(style) {
|
|
294
|
+
return new MeshBasicMaterial2({
|
|
295
|
+
color: new Color2(style.color).convertLinearToSRGB(),
|
|
296
|
+
side: BackSide,
|
|
297
|
+
transparent: true,
|
|
298
|
+
opacity: style.opacity
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
var ignoredMaterial = new MeshBasicMaterial2({
|
|
302
|
+
color: new Color2(bodyMaskColors.ignored).convertLinearToSRGB()
|
|
49
303
|
});
|
|
50
|
-
var
|
|
304
|
+
var ignoredMaterialHint = newHintMaterial(hintMaskStyles.ignored);
|
|
305
|
+
var invisibleMaterial = new MeshBasicMaterial2({
|
|
51
306
|
visible: false
|
|
52
307
|
});
|
|
53
|
-
var orientedMaterial = new
|
|
54
|
-
color:
|
|
55
|
-
});
|
|
56
|
-
var orientedMaterialHint = new MeshBasicMaterial({
|
|
57
|
-
color: 4513228,
|
|
58
|
-
side: BackSide,
|
|
59
|
-
transparent: true,
|
|
60
|
-
opacity: 0.5
|
|
61
|
-
});
|
|
62
|
-
var experimentalOriented2Material = new MeshBasicMaterial({
|
|
63
|
-
color: 16776618
|
|
64
|
-
});
|
|
65
|
-
var experimentalOriented2MaterialHint = new MeshBasicMaterial({
|
|
66
|
-
color: 16775545,
|
|
67
|
-
side: BackSide,
|
|
68
|
-
transparent: true,
|
|
69
|
-
opacity: 0.5
|
|
308
|
+
var orientedMaterial = new MeshBasicMaterial2({
|
|
309
|
+
color: bodyMaskColors.oriented
|
|
70
310
|
});
|
|
71
|
-
var
|
|
72
|
-
|
|
311
|
+
var orientedMaterialHint = newHintMaterial(hintMaskStyles.oriented);
|
|
312
|
+
var experimentalOriented2Material = new MeshBasicMaterial2({
|
|
313
|
+
color: bodyMaskColors.experimentalOriented2
|
|
73
314
|
});
|
|
74
|
-
var
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
315
|
+
var experimentalOriented2MaterialHint = newHintMaterial(
|
|
316
|
+
hintMaskStyles.experimentalOriented2
|
|
317
|
+
);
|
|
318
|
+
var mysteryMaterial = new MeshBasicMaterial2({
|
|
319
|
+
color: bodyMaskColors.mystery
|
|
79
320
|
});
|
|
321
|
+
var mysterMaterialHint = newHintMaterial(hintMaskStyles.mystery);
|
|
322
|
+
var internalBodyMaterial = newBodyMaterial(bodyMaskColors.internal);
|
|
323
|
+
var ignoredBodyMaterial = newBodyMaterial(bodyMaskColors.ignored);
|
|
324
|
+
var orientedBodyMaterial = newBodyMaterial(bodyMaskColors.oriented);
|
|
325
|
+
var experimentalOriented2BodyMaterial = newBodyMaterial(
|
|
326
|
+
bodyMaskColors.experimentalOriented2
|
|
327
|
+
);
|
|
328
|
+
var mysteryBodyMaterial = newBodyMaterial(bodyMaskColors.mystery);
|
|
80
329
|
var AxisInfo = class {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
330
|
+
vector;
|
|
331
|
+
fromZ;
|
|
332
|
+
stickerMaterial;
|
|
333
|
+
hintStickerMaterial;
|
|
334
|
+
bodyMaterial;
|
|
335
|
+
constructor(style) {
|
|
336
|
+
const { color, dimColor, hintOpacityScale } = style;
|
|
337
|
+
this.vector = style.vector;
|
|
338
|
+
this.fromZ = style.fromZ;
|
|
339
|
+
const colorLinearSRGB = new Color2(color).convertLinearToSRGB();
|
|
340
|
+
const dimColorLinearSRGB = new Color2(dimColor).convertLinearToSRGB();
|
|
89
341
|
this.stickerMaterial = {
|
|
90
|
-
regular: new
|
|
342
|
+
regular: new MeshBasicMaterial2({
|
|
91
343
|
color: colorLinearSRGB,
|
|
92
344
|
side: FrontSide
|
|
93
345
|
// TODO: set to `DoubleSide` when hint facelets are disabled.
|
|
94
346
|
}),
|
|
95
|
-
dim: new
|
|
347
|
+
dim: new MeshBasicMaterial2({
|
|
96
348
|
color: dimColorLinearSRGB,
|
|
97
349
|
side: FrontSide
|
|
98
350
|
// TODO: set to `DoubleSide` when hint facelets are disabled.
|
|
@@ -104,18 +356,12 @@ var AxisInfo = class {
|
|
|
104
356
|
mystery: mysteryMaterial
|
|
105
357
|
};
|
|
106
358
|
this.hintStickerMaterial = {
|
|
107
|
-
regular:
|
|
108
|
-
color:
|
|
109
|
-
side: BackSide,
|
|
110
|
-
transparent: true,
|
|
359
|
+
regular: newHintMaterial({
|
|
360
|
+
color: style.hintColor,
|
|
111
361
|
opacity: 0.5 * hintOpacityScale
|
|
112
362
|
}),
|
|
113
|
-
dim:
|
|
114
|
-
color:
|
|
115
|
-
options?.hintDimColor ?? dimColor
|
|
116
|
-
).convertLinearToSRGB(),
|
|
117
|
-
side: BackSide,
|
|
118
|
-
transparent: true,
|
|
363
|
+
dim: newHintMaterial({
|
|
364
|
+
color: style.hintDimColor,
|
|
119
365
|
opacity: 0.5 * hintOpacityScale
|
|
120
366
|
}),
|
|
121
367
|
oriented: orientedMaterialHint,
|
|
@@ -124,64 +370,20 @@ var AxisInfo = class {
|
|
|
124
370
|
invisible: invisibleMaterial,
|
|
125
371
|
mystery: mysterMaterialHint
|
|
126
372
|
};
|
|
373
|
+
this.bodyMaterial = {
|
|
374
|
+
regular: newBodyMaterial(colorLinearSRGB),
|
|
375
|
+
dim: newBodyMaterial(dimColorLinearSRGB),
|
|
376
|
+
oriented: orientedBodyMaterial,
|
|
377
|
+
experimentalOriented2: experimentalOriented2BodyMaterial,
|
|
378
|
+
ignored: ignoredBodyMaterial,
|
|
379
|
+
// A solid piece of plastic can't have a hole punched in it, so an
|
|
380
|
+
// invisible facelet falls back to the internal plastic color.
|
|
381
|
+
invisible: internalBodyMaterial,
|
|
382
|
+
mystery: mysteryBodyMaterial
|
|
383
|
+
};
|
|
127
384
|
}
|
|
128
|
-
vector;
|
|
129
|
-
fromZ;
|
|
130
|
-
color;
|
|
131
|
-
dimColor;
|
|
132
|
-
hintOpacityScale;
|
|
133
|
-
stickerMaterial;
|
|
134
|
-
hintStickerMaterial;
|
|
135
385
|
};
|
|
136
|
-
var axesInfo =
|
|
137
|
-
new AxisInfo(
|
|
138
|
-
new Vector3(0, 1, 0),
|
|
139
|
-
new Euler(-TAU / 4, 0, 0),
|
|
140
|
-
16777215,
|
|
141
|
-
14540253,
|
|
142
|
-
1.25
|
|
143
|
-
),
|
|
144
|
-
new AxisInfo(
|
|
145
|
-
new Vector3(-1, 0, 0),
|
|
146
|
-
new Euler(0, -TAU / 4, 0),
|
|
147
|
-
16750848,
|
|
148
|
-
8934656,
|
|
149
|
-
1,
|
|
150
|
-
{ hintDimColor: 8930304 }
|
|
151
|
-
),
|
|
152
|
-
new AxisInfo(
|
|
153
|
-
new Vector3(0, 0, 1),
|
|
154
|
-
new Euler(0, 0, 0),
|
|
155
|
-
65280,
|
|
156
|
-
34816,
|
|
157
|
-
1,
|
|
158
|
-
{ hintDimColor: 39168 }
|
|
159
|
-
),
|
|
160
|
-
new AxisInfo(
|
|
161
|
-
new Vector3(1, 0, 0),
|
|
162
|
-
new Euler(0, TAU / 4, 0),
|
|
163
|
-
16711680,
|
|
164
|
-
6684672,
|
|
165
|
-
1,
|
|
166
|
-
{ hintDimColor: 6684672 }
|
|
167
|
-
),
|
|
168
|
-
new AxisInfo(
|
|
169
|
-
new Vector3(0, 0, -1),
|
|
170
|
-
new Euler(0, TAU / 2, 0),
|
|
171
|
-
2254591,
|
|
172
|
-
1127304,
|
|
173
|
-
0.75,
|
|
174
|
-
{ hintDimColor: 6246 }
|
|
175
|
-
),
|
|
176
|
-
new AxisInfo(
|
|
177
|
-
new Vector3(0, -1, 0),
|
|
178
|
-
new Euler(TAU / 4, 0, 0),
|
|
179
|
-
16776960,
|
|
180
|
-
8947712,
|
|
181
|
-
1.25,
|
|
182
|
-
{ hintDimColor: 14540032 }
|
|
183
|
-
)
|
|
184
|
-
];
|
|
386
|
+
var axesInfo = cubeFaceStyles.map((style) => new AxisInfo(style));
|
|
185
387
|
var face = {
|
|
186
388
|
U: 0,
|
|
187
389
|
L: 1,
|
|
@@ -226,11 +428,19 @@ var cubieDimensions = {
|
|
|
226
428
|
// stickerWidth: 0.85, // Now `faceletScale` in options.
|
|
227
429
|
stickerElevation: 0.503,
|
|
228
430
|
foundationWidth: 1,
|
|
229
|
-
defaultHintStickerElevation: 1.45
|
|
431
|
+
defaultHintStickerElevation: 1.45,
|
|
432
|
+
/**
|
|
433
|
+
* How far back the eight corners of a `stickerless` cubie are shaved, from 0
|
|
434
|
+
* (not at all) to 1 (all the way back to the edges). This is the only knob
|
|
435
|
+
* that widens the notch where four pieces meet without also widening the
|
|
436
|
+
* straight dividing lines.
|
|
437
|
+
*/
|
|
438
|
+
bodyVertexCut: 0.45
|
|
230
439
|
};
|
|
231
440
|
var EXPERIMENTAL_PICTURE_CUBE_HINT_ELEVATION = 2;
|
|
232
441
|
var cube3DOptionsDefaults = {
|
|
233
442
|
showMainStickers: true,
|
|
443
|
+
experimentalCubieStyle: "stickerless",
|
|
234
444
|
hintFacelets: "floating",
|
|
235
445
|
showFoundation: true,
|
|
236
446
|
experimentalStickeringMask: void 0,
|
|
@@ -247,12 +457,12 @@ function getFaceletScale(options) {
|
|
|
247
457
|
}
|
|
248
458
|
return options.faceletScale;
|
|
249
459
|
}
|
|
250
|
-
var blackMesh = new
|
|
460
|
+
var blackMesh = new MeshBasicMaterial2({
|
|
251
461
|
color: 0,
|
|
252
462
|
opacity: 1,
|
|
253
463
|
transparent: true
|
|
254
464
|
});
|
|
255
|
-
var blackTranslucentMesh = new
|
|
465
|
+
var blackTranslucentMesh = new MeshBasicMaterial2({
|
|
256
466
|
color: 0,
|
|
257
467
|
opacity: 0.3,
|
|
258
468
|
transparent: true
|
|
@@ -275,18 +485,18 @@ function t(v, t4) {
|
|
|
275
485
|
return new Quaternion().setFromAxisAngle(v, TAU * t4 / 4);
|
|
276
486
|
}
|
|
277
487
|
var r = {
|
|
278
|
-
O: new
|
|
279
|
-
U: new
|
|
280
|
-
L: new
|
|
281
|
-
F: new
|
|
282
|
-
R: new
|
|
283
|
-
B: new
|
|
284
|
-
D: new
|
|
488
|
+
O: new Vector33(0, 0, 0),
|
|
489
|
+
U: new Vector33(0, -1, 0),
|
|
490
|
+
L: new Vector33(1, 0, 0),
|
|
491
|
+
F: new Vector33(0, 0, -1),
|
|
492
|
+
R: new Vector33(-1, 0, 0),
|
|
493
|
+
B: new Vector33(0, 0, 1),
|
|
494
|
+
D: new Vector33(0, 1, 0)
|
|
285
495
|
};
|
|
286
496
|
var firstPiecePosition = {
|
|
287
|
-
EDGES: new
|
|
288
|
-
CORNERS: new
|
|
289
|
-
CENTERS: new
|
|
497
|
+
EDGES: new Vector33(0, 1, 1),
|
|
498
|
+
CORNERS: new Vector33(1, 1, 1),
|
|
499
|
+
CENTERS: new Vector33(0, 1, 0)
|
|
290
500
|
};
|
|
291
501
|
var orientationRotation = {
|
|
292
502
|
EDGES: [0, 1].map(
|
|
@@ -344,6 +554,9 @@ var pieceDefs = {
|
|
|
344
554
|
]
|
|
345
555
|
};
|
|
346
556
|
var CUBE_SCALE = 1 / 3;
|
|
557
|
+
function cubeScale(stickerless) {
|
|
558
|
+
return stickerless ? 0.5 / cubieBodyHalfExtent(3) : CUBE_SCALE;
|
|
559
|
+
}
|
|
347
560
|
var pictureStickerCoords = {
|
|
348
561
|
EDGES: [
|
|
349
562
|
[
|
|
@@ -455,11 +668,11 @@ function sharedCubieFoundationGeometry() {
|
|
|
455
668
|
));
|
|
456
669
|
}
|
|
457
670
|
function newStickerGeometry() {
|
|
458
|
-
const r2 = new
|
|
671
|
+
const r2 = new BufferGeometry2();
|
|
459
672
|
const half = 0.5;
|
|
460
673
|
r2.setAttribute(
|
|
461
674
|
"position",
|
|
462
|
-
new
|
|
675
|
+
new BufferAttribute2(
|
|
463
676
|
new Float32Array([
|
|
464
677
|
half,
|
|
465
678
|
half,
|
|
@@ -485,7 +698,7 @@ function newStickerGeometry() {
|
|
|
485
698
|
);
|
|
486
699
|
r2.setAttribute(
|
|
487
700
|
"uv",
|
|
488
|
-
new
|
|
701
|
+
new BufferAttribute2(
|
|
489
702
|
new Float32Array([
|
|
490
703
|
1,
|
|
491
704
|
1,
|
|
@@ -517,6 +730,26 @@ function newStickerGeometry() {
|
|
|
517
730
|
);
|
|
518
731
|
return r2;
|
|
519
732
|
}
|
|
733
|
+
var cubieBodyGeometryCache = /* @__PURE__ */ new Map();
|
|
734
|
+
function cubieBodyGeometry(orbit, outerAxes) {
|
|
735
|
+
const cached = cubieBodyGeometryCache.get(orbit);
|
|
736
|
+
if (cached) {
|
|
737
|
+
return cached;
|
|
738
|
+
}
|
|
739
|
+
const scale = cubieBodyDimensions.pieceScale;
|
|
740
|
+
const geometry = beveledCubieGeometry(
|
|
741
|
+
axesInfo.map((axisInfo) => axisInfo.vector),
|
|
742
|
+
outerAxes,
|
|
743
|
+
cubieBodyDimensions.halfWidth * scale,
|
|
744
|
+
cubieBodyDimensions.outerAxisRadius * scale,
|
|
745
|
+
cubieBodyDimensions.innerEdgeRadius * scale,
|
|
746
|
+
cubieBodyDimensions.innerCornerRadius * scale,
|
|
747
|
+
cubieBodyDimensions.cornerSharpness,
|
|
748
|
+
cubieBodyDimensions.roundingSegments
|
|
749
|
+
);
|
|
750
|
+
cubieBodyGeometryCache.set(orbit, geometry);
|
|
751
|
+
return geometry;
|
|
752
|
+
}
|
|
520
753
|
var sharedStickerGeometryCache;
|
|
521
754
|
function sharedStickerGeometry() {
|
|
522
755
|
return sharedStickerGeometryCache ??= newStickerGeometry();
|
|
@@ -547,7 +780,8 @@ var Cube3D = class extends Object3D {
|
|
|
547
780
|
this.createCubie.bind(this, orbit, orbitFaceletInfo)
|
|
548
781
|
);
|
|
549
782
|
}
|
|
550
|
-
|
|
783
|
+
const scale = cubeScale(this.#stickerless());
|
|
784
|
+
this.scale.set(scale, scale, scale);
|
|
551
785
|
if (this.options.experimentalStickeringMask) {
|
|
552
786
|
this.setStickeringMask(this.options.experimentalStickeringMask);
|
|
553
787
|
}
|
|
@@ -654,12 +888,24 @@ var Cube3D = class extends Object3D {
|
|
|
654
888
|
experimentalSetHintStickerSpriteURL(hintStickerSpriteURL) {
|
|
655
889
|
this.#setHintSpriteURL?.(hintStickerSpriteURL);
|
|
656
890
|
}
|
|
891
|
+
#stickerless() {
|
|
892
|
+
return this.options.experimentalCubieStyle === "stickerless";
|
|
893
|
+
}
|
|
894
|
+
#setFaceletMaterial(faceletInfo, mask) {
|
|
895
|
+
const axisInfo = axesInfo[faceletInfo.faceIdx];
|
|
896
|
+
const { bodyMaterialIndex } = faceletInfo;
|
|
897
|
+
if (bodyMaterialIndex === void 0) {
|
|
898
|
+
faceletInfo.facelet.material = axisInfo.stickerMaterial[mask];
|
|
899
|
+
} else {
|
|
900
|
+
faceletInfo.facelet.material[bodyMaterialIndex] = axisInfo.bodyMaterial[mask];
|
|
901
|
+
}
|
|
902
|
+
}
|
|
657
903
|
setStickeringMask(stickeringMask) {
|
|
658
904
|
if (stickeringMask.specialBehaviour === "picture") {
|
|
659
905
|
for (const pieceInfos of Object.values(this.kpuzzleFaceletInfo)) {
|
|
660
906
|
for (const faceletInfos of pieceInfos) {
|
|
661
907
|
for (const faceletInfo of faceletInfos) {
|
|
662
|
-
faceletInfo
|
|
908
|
+
this.#setFaceletMaterial(faceletInfo, "invisible");
|
|
663
909
|
const { hintFacelet } = faceletInfo;
|
|
664
910
|
if (hintFacelet) {
|
|
665
911
|
hintFacelet.material = invisibleMaterial;
|
|
@@ -682,7 +928,7 @@ var Cube3D = class extends Object3D {
|
|
|
682
928
|
if (faceletStickeringMask) {
|
|
683
929
|
const faceletInfo = pieceInfo[faceletIdx];
|
|
684
930
|
const stickeringMask2 = typeof faceletStickeringMask === "string" ? faceletStickeringMask : faceletStickeringMask?.mask;
|
|
685
|
-
faceletInfo
|
|
931
|
+
this.#setFaceletMaterial(faceletInfo, stickeringMask2);
|
|
686
932
|
const hintStickeringMask = typeof faceletStickeringMask === "string" ? stickeringMask2 : faceletStickeringMask.hintMask ?? stickeringMask2;
|
|
687
933
|
if (faceletInfo.hintFacelet) {
|
|
688
934
|
faceletInfo.hintFacelet.material = axesInfo[faceletInfo.faceIdx].hintStickerMaterial[hintStickeringMask];
|
|
@@ -769,26 +1015,39 @@ var Cube3D = class extends Object3D {
|
|
|
769
1015
|
const cubieFaceletInfo = [];
|
|
770
1016
|
orbitFacelets.push(cubieFaceletInfo);
|
|
771
1017
|
const cubie = new Group();
|
|
772
|
-
|
|
1018
|
+
const body = this.#stickerless() ? new Mesh(
|
|
1019
|
+
cubieBodyGeometry(
|
|
1020
|
+
orbit,
|
|
1021
|
+
cubieStickerOrder.slice(0, piece.stickerFaces.length)
|
|
1022
|
+
),
|
|
1023
|
+
axesInfo.map(() => internalBodyMaterial)
|
|
1024
|
+
) : null;
|
|
1025
|
+
if (body) {
|
|
1026
|
+
cubie.add(body);
|
|
1027
|
+
} else if (this.options.showFoundation) {
|
|
773
1028
|
const foundation = this.createCubieFoundation();
|
|
774
1029
|
cubie.add(foundation);
|
|
775
1030
|
this.experimentalFoundationMeshes.push(foundation);
|
|
776
1031
|
}
|
|
777
1032
|
for (let i = 0; i < piece.stickerFaces.length; i++) {
|
|
778
|
-
const
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
1033
|
+
const faceIdx = piece.stickerFaces[i];
|
|
1034
|
+
const faceletInfo = body ? { faceIdx, facelet: body, bodyMaterialIndex: cubieStickerOrder[i] } : {
|
|
1035
|
+
faceIdx,
|
|
1036
|
+
facelet: this.createSticker(
|
|
1037
|
+
axesInfo[cubieStickerOrder[i]],
|
|
1038
|
+
axesInfo[faceIdx],
|
|
1039
|
+
false
|
|
1040
|
+
)
|
|
786
1041
|
};
|
|
787
|
-
|
|
1042
|
+
if (body) {
|
|
1043
|
+
body.material[cubieStickerOrder[i]] = axesInfo[faceIdx].bodyMaterial.regular;
|
|
1044
|
+
} else {
|
|
1045
|
+
cubie.add(faceletInfo.facelet);
|
|
1046
|
+
}
|
|
788
1047
|
if (this.options.hintFacelets === "floating") {
|
|
789
1048
|
const hintSticker = this.createSticker(
|
|
790
1049
|
axesInfo[cubieStickerOrder[i]],
|
|
791
|
-
axesInfo[
|
|
1050
|
+
axesInfo[faceIdx],
|
|
792
1051
|
true
|
|
793
1052
|
);
|
|
794
1053
|
cubie.add(hintSticker);
|
|
@@ -805,7 +1064,7 @@ var Cube3D = class extends Object3D {
|
|
|
805
1064
|
axesInfo[piece.stickerFaces[i]],
|
|
806
1065
|
hint
|
|
807
1066
|
);
|
|
808
|
-
mesh.material = new
|
|
1067
|
+
mesh.material = new MeshBasicMaterial2({
|
|
809
1068
|
map: texture,
|
|
810
1069
|
side: hint ? BackSide : DoubleSide,
|
|
811
1070
|
transparent: true
|
|
@@ -834,7 +1093,7 @@ var Cube3D = class extends Object3D {
|
|
|
834
1093
|
}
|
|
835
1094
|
mesh.geometry.setAttribute(
|
|
836
1095
|
"uv",
|
|
837
|
-
new
|
|
1096
|
+
new BufferAttribute2(
|
|
838
1097
|
new Float32Array([
|
|
839
1098
|
v3.x,
|
|
840
1099
|
v3.y,
|
|
@@ -904,6 +1163,9 @@ var Cube3D = class extends Object3D {
|
|
|
904
1163
|
}
|
|
905
1164
|
/** @deprecated */
|
|
906
1165
|
experimentalSetFoundationOpacity(opacity) {
|
|
1166
|
+
if (this.experimentalFoundationMeshes.length === 0) {
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
907
1169
|
this.experimentalFoundationMeshes[0].material.opacity = opacity;
|
|
908
1170
|
}
|
|
909
1171
|
/** @deprecated */
|
|
@@ -913,8 +1175,10 @@ var Cube3D = class extends Object3D {
|
|
|
913
1175
|
for (const pieceInfo of orbitInfo) {
|
|
914
1176
|
for (const faceletInfo of pieceInfo) {
|
|
915
1177
|
const scale = getFaceletScale(this.options);
|
|
916
|
-
faceletInfo.
|
|
917
|
-
|
|
1178
|
+
if (faceletInfo.bodyMaterialIndex === void 0) {
|
|
1179
|
+
faceletInfo.facelet.scale.setX(scale);
|
|
1180
|
+
faceletInfo.facelet.scale.setY(scale);
|
|
1181
|
+
}
|
|
918
1182
|
faceletInfo.hintFacelet?.scale.setX(scale);
|
|
919
1183
|
faceletInfo.hintFacelet?.scale.setY(scale);
|
|
920
1184
|
}
|
|
@@ -940,24 +1204,361 @@ var Cube3D = class extends Object3D {
|
|
|
940
1204
|
}
|
|
941
1205
|
};
|
|
942
1206
|
|
|
1207
|
+
// src/cubing/twisty/views/3D/puzzles/CubePieces.ts
|
|
1208
|
+
import { BufferAttribute as BufferAttribute3 } from "three/src/core/BufferAttribute.js";
|
|
1209
|
+
import { BufferGeometry as BufferGeometry3 } from "three/src/core/BufferGeometry.js";
|
|
1210
|
+
import { Matrix4 as Matrix42 } from "three/src/math/Matrix4.js";
|
|
1211
|
+
import { Vector3 as Vector34 } from "three/src/math/Vector3.js";
|
|
1212
|
+
|
|
1213
|
+
// src/cubing/twisty/views/3D/puzzles/StickerlessPlan.ts
|
|
1214
|
+
var HINT_FACELET_ELEVATION = 0.5;
|
|
1215
|
+
var HINT_FACELET_SCALE = 0.85;
|
|
1216
|
+
var faceletMeshStickeringMasks = [
|
|
1217
|
+
"regular",
|
|
1218
|
+
"dim",
|
|
1219
|
+
"oriented",
|
|
1220
|
+
"experimentalOriented2",
|
|
1221
|
+
"ignored",
|
|
1222
|
+
"invisible",
|
|
1223
|
+
"mystery"
|
|
1224
|
+
];
|
|
1225
|
+
function bodyColor(style, mask) {
|
|
1226
|
+
switch (mask) {
|
|
1227
|
+
case "regular":
|
|
1228
|
+
return style.color;
|
|
1229
|
+
case "dim":
|
|
1230
|
+
return style.dimColor;
|
|
1231
|
+
// A solid piece of plastic can't have a hole punched in it, so an invisible
|
|
1232
|
+
// facelet falls back to the internal plastic color.
|
|
1233
|
+
case "invisible":
|
|
1234
|
+
return bodyMaskColors.internal;
|
|
1235
|
+
default:
|
|
1236
|
+
return bodyMaskColors[mask];
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
function hintColor(style, mask) {
|
|
1240
|
+
switch (mask) {
|
|
1241
|
+
case "regular":
|
|
1242
|
+
return { color: style.hintColor, opacity: 0.5 * style.hintOpacityScale };
|
|
1243
|
+
case "dim":
|
|
1244
|
+
return {
|
|
1245
|
+
color: style.hintDimColor,
|
|
1246
|
+
opacity: 0.5 * style.hintOpacityScale
|
|
1247
|
+
};
|
|
1248
|
+
case "invisible":
|
|
1249
|
+
return { color: 0, opacity: 0 };
|
|
1250
|
+
default:
|
|
1251
|
+
return hintMaskStyles[mask];
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
function faceletAppearanceKey(faceStyle, mask, hintMask) {
|
|
1255
|
+
return (faceStyle * faceletMeshStickeringMasks.length + faceletMeshStickeringMasks.indexOf(mask)) * faceletMeshStickeringMasks.length + faceletMeshStickeringMasks.indexOf(hintMask);
|
|
1256
|
+
}
|
|
1257
|
+
function faceletAppearance(cache, styles, faceStyle, mask, hintMask) {
|
|
1258
|
+
const key = faceletAppearanceKey(faceStyle, mask, hintMask);
|
|
1259
|
+
const cached = cache.get(key);
|
|
1260
|
+
if (cached) {
|
|
1261
|
+
return cached;
|
|
1262
|
+
}
|
|
1263
|
+
const style = styles[faceStyle];
|
|
1264
|
+
const body = bodyColor(style, mask);
|
|
1265
|
+
const hint = hintColor(style, hintMask);
|
|
1266
|
+
const appearance = new Uint8Array([
|
|
1267
|
+
body >> 16 & 255,
|
|
1268
|
+
body >> 8 & 255,
|
|
1269
|
+
body & 255,
|
|
1270
|
+
255,
|
|
1271
|
+
hint.color >> 16 & 255,
|
|
1272
|
+
hint.color >> 8 & 255,
|
|
1273
|
+
hint.color & 255,
|
|
1274
|
+
Math.round(hint.opacity * 255)
|
|
1275
|
+
]);
|
|
1276
|
+
cache.set(key, appearance);
|
|
1277
|
+
return appearance;
|
|
1278
|
+
}
|
|
1279
|
+
function writeColor(colors, ranges, appearance, offset = 0) {
|
|
1280
|
+
const array = colors.array;
|
|
1281
|
+
const red = appearance[offset];
|
|
1282
|
+
const green = appearance[offset + 1];
|
|
1283
|
+
const blue = appearance[offset + 2];
|
|
1284
|
+
const alpha = appearance[offset + 3];
|
|
1285
|
+
for (const range of ranges) {
|
|
1286
|
+
for (let vertex = range.start; vertex < range.start + range.count; vertex++) {
|
|
1287
|
+
array[4 * vertex] = red;
|
|
1288
|
+
array[4 * vertex + 1] = green;
|
|
1289
|
+
array[4 * vertex + 2] = blue;
|
|
1290
|
+
array[4 * vertex + 3] = alpha;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
function paintInternal(colors, ranges) {
|
|
1295
|
+
const internal = bodyMaskColors.internal;
|
|
1296
|
+
writeColor(
|
|
1297
|
+
colors,
|
|
1298
|
+
ranges,
|
|
1299
|
+
new Uint8Array([
|
|
1300
|
+
internal >> 16 & 255,
|
|
1301
|
+
internal >> 8 & 255,
|
|
1302
|
+
internal & 255,
|
|
1303
|
+
255
|
|
1304
|
+
])
|
|
1305
|
+
);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// src/cubing/twisty/views/3D/puzzles/CubePieces.ts
|
|
1309
|
+
var PG_SCALE = 0.5;
|
|
1310
|
+
var FACE_COUNT = 6;
|
|
1311
|
+
function faceIdxForNormal(x, y, z) {
|
|
1312
|
+
for (let faceIdx = 0; faceIdx < FACE_COUNT; faceIdx++) {
|
|
1313
|
+
const { vector } = cubeFaceStyles[faceIdx];
|
|
1314
|
+
if (vector.x * x + vector.y * y + vector.z * z > 0.5) {
|
|
1315
|
+
return faceIdx;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
return -1;
|
|
1319
|
+
}
|
|
1320
|
+
function cubeLayout(stickerDat) {
|
|
1321
|
+
const { stickers } = stickerDat;
|
|
1322
|
+
if (stickerDat.faces.length !== FACE_COUNT || stickers.length === 0) {
|
|
1323
|
+
return null;
|
|
1324
|
+
}
|
|
1325
|
+
let faceletCount = 0;
|
|
1326
|
+
let halfExtent = 0;
|
|
1327
|
+
for (const sticker of stickers) {
|
|
1328
|
+
if (sticker.coords.length !== 12) {
|
|
1329
|
+
return null;
|
|
1330
|
+
}
|
|
1331
|
+
if (!sticker.isDup) {
|
|
1332
|
+
faceletCount++;
|
|
1333
|
+
}
|
|
1334
|
+
for (const coord of sticker.coords) {
|
|
1335
|
+
halfExtent = Math.max(halfExtent, Math.abs(coord));
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
const layers = Math.round(Math.sqrt(faceletCount / FACE_COUNT));
|
|
1339
|
+
if (FACE_COUNT * layers * layers !== faceletCount || layers < 2) {
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
const slotWidth = 2 * halfExtent / layers;
|
|
1343
|
+
const epsilon = slotWidth / 1e3;
|
|
1344
|
+
const faceColors = new Array(FACE_COUNT);
|
|
1345
|
+
const cubiesByPiece = /* @__PURE__ */ new Map();
|
|
1346
|
+
const cubies = [];
|
|
1347
|
+
const centroid = [0, 0, 0];
|
|
1348
|
+
for (const sticker of stickers) {
|
|
1349
|
+
const { coords } = sticker;
|
|
1350
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
1351
|
+
centroid[axis] = (coords[axis] + coords[axis + 3] + coords[axis + 6] + coords[axis + 9]) / 4;
|
|
1352
|
+
}
|
|
1353
|
+
let normalAxis = 0;
|
|
1354
|
+
for (let axis = 1; axis < 3; axis++) {
|
|
1355
|
+
if (Math.abs(centroid[axis]) > Math.abs(centroid[normalAxis])) {
|
|
1356
|
+
normalAxis = axis;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
const outward = Math.sign(centroid[normalAxis]);
|
|
1360
|
+
const center = new Vector34();
|
|
1361
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
1362
|
+
let expected;
|
|
1363
|
+
if (axis === normalAxis) {
|
|
1364
|
+
expected = outward * halfExtent;
|
|
1365
|
+
center.setComponent(axis, outward * (halfExtent - slotWidth / 2));
|
|
1366
|
+
} else {
|
|
1367
|
+
const slot = Math.round(
|
|
1368
|
+
(centroid[axis] + halfExtent) / slotWidth - 0.5
|
|
1369
|
+
);
|
|
1370
|
+
if (slot < 0 || slot >= layers) {
|
|
1371
|
+
return null;
|
|
1372
|
+
}
|
|
1373
|
+
expected = -halfExtent + (slot + 0.5) * slotWidth;
|
|
1374
|
+
center.setComponent(axis, expected);
|
|
1375
|
+
}
|
|
1376
|
+
if (Math.abs(centroid[axis] - expected) > epsilon) {
|
|
1377
|
+
return null;
|
|
1378
|
+
}
|
|
1379
|
+
const halfSpan = axis === normalAxis ? 0 : slotWidth / 2;
|
|
1380
|
+
for (let vertex = 0; vertex < 4; vertex++) {
|
|
1381
|
+
const offset = Math.abs(coords[3 * vertex + axis] - expected);
|
|
1382
|
+
if (Math.abs(offset - halfSpan) > epsilon) {
|
|
1383
|
+
return null;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
const faceIdx = faceIdxForNormal(
|
|
1388
|
+
normalAxis === 0 ? outward : 0,
|
|
1389
|
+
normalAxis === 1 ? outward : 0,
|
|
1390
|
+
normalAxis === 2 ? outward : 0
|
|
1391
|
+
);
|
|
1392
|
+
if (faceIdx === -1) {
|
|
1393
|
+
return null;
|
|
1394
|
+
}
|
|
1395
|
+
faceColors[faceIdx] ??= sticker.color;
|
|
1396
|
+
if (faceColors[faceIdx] !== sticker.color) {
|
|
1397
|
+
return null;
|
|
1398
|
+
}
|
|
1399
|
+
const pieceKey = `${sticker.orbit}/${sticker.ord}`;
|
|
1400
|
+
let cubie = cubiesByPiece.get(pieceKey);
|
|
1401
|
+
if (!cubie) {
|
|
1402
|
+
cubie = {
|
|
1403
|
+
orbit: sticker.orbit,
|
|
1404
|
+
ord: sticker.ord,
|
|
1405
|
+
center,
|
|
1406
|
+
outwardFaces: [],
|
|
1407
|
+
stickers: []
|
|
1408
|
+
};
|
|
1409
|
+
cubiesByPiece.set(pieceKey, cubie);
|
|
1410
|
+
cubies.push(cubie);
|
|
1411
|
+
} else if (cubie.center.distanceTo(center) > epsilon) {
|
|
1412
|
+
return null;
|
|
1413
|
+
}
|
|
1414
|
+
if (!cubie.outwardFaces.includes(faceIdx)) {
|
|
1415
|
+
cubie.outwardFaces.push(faceIdx);
|
|
1416
|
+
cubie.outwardFaces.sort((a, b) => a - b);
|
|
1417
|
+
}
|
|
1418
|
+
cubie.stickers.push({ faceIdx, ori: sticker.ori, isDup: !!sticker.isDup });
|
|
1419
|
+
}
|
|
1420
|
+
if (new Set(faceColors).size !== FACE_COUNT) {
|
|
1421
|
+
return null;
|
|
1422
|
+
}
|
|
1423
|
+
return { layers, halfExtent, cubies };
|
|
1424
|
+
}
|
|
1425
|
+
function newCubieShape(outwardFaces, slotWidth) {
|
|
1426
|
+
const scale = cubieBodyDimensions.pieceScale * slotWidth;
|
|
1427
|
+
const body = beveledCubieGeometry(
|
|
1428
|
+
cubeFaceStyles.map((style) => style.vector),
|
|
1429
|
+
outwardFaces,
|
|
1430
|
+
cubieBodyDimensions.halfWidth * scale,
|
|
1431
|
+
cubieBodyDimensions.outerAxisRadius * scale,
|
|
1432
|
+
cubieBodyDimensions.innerEdgeRadius * scale,
|
|
1433
|
+
cubieBodyDimensions.innerCornerRadius * scale,
|
|
1434
|
+
cubieBodyDimensions.cornerSharpness,
|
|
1435
|
+
cubieBodyDimensions.roundingSegments
|
|
1436
|
+
);
|
|
1437
|
+
const bodyPositions = body.getAttribute("position").array;
|
|
1438
|
+
const bodyIndices = Array.from(body.getIndex().array);
|
|
1439
|
+
const faceVertexRanges = [];
|
|
1440
|
+
for (const group of body.groups) {
|
|
1441
|
+
let start = Number.POSITIVE_INFINITY;
|
|
1442
|
+
let end = 0;
|
|
1443
|
+
for (let i = group.start; i < group.start + group.count; i++) {
|
|
1444
|
+
start = Math.min(start, bodyIndices[i]);
|
|
1445
|
+
end = Math.max(end, bodyIndices[i] + 1);
|
|
1446
|
+
}
|
|
1447
|
+
faceVertexRanges[group.materialIndex] = { start, count: end - start };
|
|
1448
|
+
}
|
|
1449
|
+
const hintPositions = [];
|
|
1450
|
+
const hintIndices = [];
|
|
1451
|
+
const hintVertexRanges = /* @__PURE__ */ new Map();
|
|
1452
|
+
const hintHalfWidth = HINT_FACELET_SCALE * slotWidth / 2;
|
|
1453
|
+
const hintDistance = slotWidth / 2 + HINT_FACELET_ELEVATION;
|
|
1454
|
+
const u = new Vector34();
|
|
1455
|
+
const v = new Vector34();
|
|
1456
|
+
const corner = new Vector34();
|
|
1457
|
+
for (const faceIdx of outwardFaces) {
|
|
1458
|
+
const normal2 = cubeFaceStyles[faceIdx].vector;
|
|
1459
|
+
u.set(normal2.y, normal2.z, normal2.x);
|
|
1460
|
+
v.crossVectors(normal2, u);
|
|
1461
|
+
const start = hintPositions.length / 3;
|
|
1462
|
+
for (const [su, sv] of [
|
|
1463
|
+
[-1, -1],
|
|
1464
|
+
[1, -1],
|
|
1465
|
+
[1, 1],
|
|
1466
|
+
[-1, 1]
|
|
1467
|
+
]) {
|
|
1468
|
+
corner.copy(normal2).multiplyScalar(hintDistance).addScaledVector(u, su * hintHalfWidth).addScaledVector(v, sv * hintHalfWidth);
|
|
1469
|
+
hintPositions.push(corner.x, corner.y, corner.z);
|
|
1470
|
+
}
|
|
1471
|
+
hintIndices.push(start, start + 1, start + 2, start, start + 2, start + 3);
|
|
1472
|
+
hintVertexRanges.set(faceIdx, { start, count: 4 });
|
|
1473
|
+
}
|
|
1474
|
+
body.dispose();
|
|
1475
|
+
return {
|
|
1476
|
+
bodyPosition: new BufferAttribute3(bodyPositions.slice(), 3),
|
|
1477
|
+
bodyIndex: new BufferAttribute3(new Uint16Array(bodyIndices), 1),
|
|
1478
|
+
bodyVertexCount: bodyPositions.length / 3,
|
|
1479
|
+
hintPosition: new BufferAttribute3(new Float32Array(hintPositions), 3),
|
|
1480
|
+
hintIndex: new BufferAttribute3(new Uint16Array(hintIndices), 1),
|
|
1481
|
+
hintVertexCount: hintPositions.length / 3,
|
|
1482
|
+
faceVertexRanges,
|
|
1483
|
+
hintVertexRanges
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
function newPieceMesh(position, index, vertexCount) {
|
|
1487
|
+
const geometry = new BufferGeometry3();
|
|
1488
|
+
geometry.setAttribute("position", position);
|
|
1489
|
+
geometry.setIndex(index);
|
|
1490
|
+
const colors = new BufferAttribute3(new Uint8Array(4 * vertexCount), 4, true);
|
|
1491
|
+
geometry.setAttribute("color", colors);
|
|
1492
|
+
return { geometry, colors };
|
|
1493
|
+
}
|
|
1494
|
+
function newCubiePiece(cubie, shape) {
|
|
1495
|
+
const body = newPieceMesh(
|
|
1496
|
+
shape.bodyPosition,
|
|
1497
|
+
shape.bodyIndex,
|
|
1498
|
+
shape.bodyVertexCount
|
|
1499
|
+
);
|
|
1500
|
+
const hint = shape.hintVertexCount > 0 ? newPieceMesh(shape.hintPosition, shape.hintIndex, shape.hintVertexCount) : null;
|
|
1501
|
+
paintInternal(body.colors, shape.faceVertexRanges);
|
|
1502
|
+
const facelets = cubie.stickers.map((sticker) => ({
|
|
1503
|
+
ori: sticker.ori,
|
|
1504
|
+
faceStyle: sticker.faceIdx,
|
|
1505
|
+
// A duplicate shares its square with the real facelet, which is the one
|
|
1506
|
+
// that gets painted.
|
|
1507
|
+
body: sticker.isDup ? [] : [shape.faceVertexRanges[sticker.faceIdx]],
|
|
1508
|
+
hint: sticker.isDup ? [] : [shape.hintVertexRanges.get(sticker.faceIdx)].filter(Boolean)
|
|
1509
|
+
}));
|
|
1510
|
+
return {
|
|
1511
|
+
orbit: cubie.orbit,
|
|
1512
|
+
ord: cubie.ord,
|
|
1513
|
+
home: new Matrix42().setPosition(cubie.center),
|
|
1514
|
+
body,
|
|
1515
|
+
hint,
|
|
1516
|
+
facelets
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
function cubePuzzlePlan(stickerDat) {
|
|
1520
|
+
const layout = cubeLayout(stickerDat);
|
|
1521
|
+
if (!layout) {
|
|
1522
|
+
return null;
|
|
1523
|
+
}
|
|
1524
|
+
const slotWidth = 2 * layout.halfExtent / layout.layers;
|
|
1525
|
+
const shapes = /* @__PURE__ */ new Map();
|
|
1526
|
+
const pieces = layout.cubies.map((cubie) => {
|
|
1527
|
+
const shapeKey = cubie.outwardFaces.join(",");
|
|
1528
|
+
let shape = shapes.get(shapeKey);
|
|
1529
|
+
if (!shape) {
|
|
1530
|
+
shape = newCubieShape(cubie.outwardFaces, slotWidth);
|
|
1531
|
+
shapes.set(shapeKey, shape);
|
|
1532
|
+
}
|
|
1533
|
+
return newCubiePiece(cubie, shape);
|
|
1534
|
+
});
|
|
1535
|
+
return {
|
|
1536
|
+
faceStyles: cubeFaceStyles,
|
|
1537
|
+
pieces,
|
|
1538
|
+
// Oversized pieces push the puzzle past the half-extent the camera framing
|
|
1539
|
+
// assumes, so scale it back to the size `PG3D` would have drawn.
|
|
1540
|
+
scale: PG_SCALE * layout.halfExtent / (cubieBodyHalfExtent(layout.layers) * slotWidth)
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
|
|
943
1544
|
// src/cubing/twisty/views/3D/puzzles/PG3D.ts
|
|
944
1545
|
import { DoubleSide as DoubleSide2, FrontSide as FrontSide2 } from "three/src/constants.js";
|
|
945
|
-
import { BufferAttribute as
|
|
946
|
-
import { BufferGeometry as
|
|
1546
|
+
import { BufferAttribute as BufferAttribute4 } from "three/src/core/BufferAttribute.js";
|
|
1547
|
+
import { BufferGeometry as BufferGeometry4 } from "three/src/core/BufferGeometry.js";
|
|
947
1548
|
import { Object3D as Object3D2 } from "three/src/core/Object3D.js";
|
|
948
|
-
import { MeshBasicMaterial as
|
|
949
|
-
import { Color as
|
|
950
|
-
import { Vector3 as
|
|
1549
|
+
import { MeshBasicMaterial as MeshBasicMaterial3 } from "three/src/materials/MeshBasicMaterial.js";
|
|
1550
|
+
import { Color as Color3 } from "three/src/math/Color.js";
|
|
1551
|
+
import { Vector3 as Vector35 } from "three/src/math/Vector3.js";
|
|
951
1552
|
import { Group as Group2 } from "three/src/objects/Group.js";
|
|
952
1553
|
import { Mesh as Mesh2 } from "three/src/objects/Mesh.js";
|
|
953
|
-
var foundationMaterial = new
|
|
1554
|
+
var foundationMaterial = new MeshBasicMaterial3({
|
|
954
1555
|
side: DoubleSide2,
|
|
955
1556
|
color: 0
|
|
956
1557
|
});
|
|
957
|
-
var invisMaterial = new
|
|
1558
|
+
var invisMaterial = new MeshBasicMaterial3({
|
|
958
1559
|
visible: false
|
|
959
1560
|
});
|
|
960
|
-
var basicStickerMaterial = new
|
|
1561
|
+
var basicStickerMaterial = new MeshBasicMaterial3({
|
|
961
1562
|
vertexColors: true
|
|
962
1563
|
});
|
|
963
1564
|
function dist(coords, a, b) {
|
|
@@ -1092,9 +1693,9 @@ var Filler = class {
|
|
|
1092
1693
|
}
|
|
1093
1694
|
}
|
|
1094
1695
|
setAttributes(geo) {
|
|
1095
|
-
geo.setAttribute("position", new
|
|
1696
|
+
geo.setAttribute("position", new BufferAttribute4(this.vertices, 3));
|
|
1096
1697
|
const sa2 = this.colors.subarray(0, 9 * this.sz);
|
|
1097
|
-
geo.setAttribute("color", new
|
|
1698
|
+
geo.setAttribute("color", new BufferAttribute4(sa2, 3, true));
|
|
1098
1699
|
}
|
|
1099
1700
|
makeGroups(geo) {
|
|
1100
1701
|
geo.clearGroups();
|
|
@@ -1129,7 +1730,7 @@ var StickerDef = class {
|
|
|
1129
1730
|
this.isDup = !!stickerDat.isDup;
|
|
1130
1731
|
this.faceNum = stickerDat.face;
|
|
1131
1732
|
this.stickerStart = filler.ipos;
|
|
1132
|
-
const sdColor = new
|
|
1733
|
+
const sdColor = new Color3(stickerDat.color).getHex();
|
|
1133
1734
|
this.origColor = sdColor;
|
|
1134
1735
|
this.origColorStickeringMask = sdColor;
|
|
1135
1736
|
if (options?.stickeringMask) {
|
|
@@ -1203,7 +1804,7 @@ var StickerDef = class {
|
|
|
1203
1804
|
if (this.origColor === 16777215) {
|
|
1204
1805
|
c = 14540253;
|
|
1205
1806
|
} else {
|
|
1206
|
-
c = new
|
|
1807
|
+
c = new Color3(this.origColor).multiplyScalar(0.5).getHex();
|
|
1207
1808
|
}
|
|
1208
1809
|
break;
|
|
1209
1810
|
}
|
|
@@ -1320,7 +1921,7 @@ var HitPlaneDef = class {
|
|
|
1320
1921
|
filler.addUncolored(coords, g);
|
|
1321
1922
|
filler.addUncolored(coords, g + 1);
|
|
1322
1923
|
}
|
|
1323
|
-
this.geo = new
|
|
1924
|
+
this.geo = new BufferGeometry4();
|
|
1324
1925
|
filler.setAttributes(this.geo);
|
|
1325
1926
|
const obj = new Mesh2(this.geo, invisMaterial);
|
|
1326
1927
|
obj.userData["quantumMove"] = stickerDat.notationMapper.notationToExternal(
|
|
@@ -1335,12 +1936,12 @@ var AxisInfo2 = class {
|
|
|
1335
1936
|
order;
|
|
1336
1937
|
constructor(axisDat) {
|
|
1337
1938
|
const vec = axisDat.coordinates;
|
|
1338
|
-
this.axis = new
|
|
1939
|
+
this.axis = new Vector35(vec[0], vec[1], vec[2]);
|
|
1339
1940
|
this.order = axisDat.order;
|
|
1340
1941
|
}
|
|
1341
1942
|
};
|
|
1342
1943
|
var DEFAULT_COLOR_FRACTION = 0.71;
|
|
1343
|
-
var
|
|
1944
|
+
var PG_SCALE2 = 0.5;
|
|
1344
1945
|
var PG3D = class extends Object3D2 {
|
|
1345
1946
|
constructor(scheduleRenderCallback, kpuzzle, stickerDat, enableFoundationOpt = false, enableHintStickersOpt = false, hintStickerHeightScale = 1, faceletScale = 1, params = {}) {
|
|
1346
1947
|
super();
|
|
@@ -1352,7 +1953,7 @@ var PG3D = class extends Object3D2 {
|
|
|
1352
1953
|
if (stickerDat.stickers.length === 0) {
|
|
1353
1954
|
throw Error("Reuse of stickerdat from pg; please don't do that.");
|
|
1354
1955
|
}
|
|
1355
|
-
this.hintMaterial = new
|
|
1956
|
+
this.hintMaterial = new MeshBasicMaterial3({
|
|
1356
1957
|
vertexColors: true,
|
|
1357
1958
|
transparent: true,
|
|
1358
1959
|
opacity: 0.5
|
|
@@ -1440,14 +2041,14 @@ var PG3D = class extends Object3D2 {
|
|
|
1440
2041
|
this.stickers[orbit][ori][ord].addFoundation(filler, sticker, black);
|
|
1441
2042
|
}
|
|
1442
2043
|
}
|
|
1443
|
-
const fixedGeo = new
|
|
2044
|
+
const fixedGeo = new BufferGeometry4();
|
|
1444
2045
|
filler.setAttributes(fixedGeo);
|
|
1445
2046
|
filler.makeGroups(fixedGeo);
|
|
1446
2047
|
const obj = new Mesh2(fixedGeo, this.materialArray1);
|
|
1447
|
-
obj.scale.set(
|
|
2048
|
+
obj.scale.set(PG_SCALE2, PG_SCALE2, PG_SCALE2);
|
|
1448
2049
|
this.add(obj);
|
|
1449
2050
|
const obj2 = new Mesh2(fixedGeo, this.materialArray2);
|
|
1450
|
-
obj2.scale.set(
|
|
2051
|
+
obj2.scale.set(PG_SCALE2, PG_SCALE2, PG_SCALE2);
|
|
1451
2052
|
this.add(obj2);
|
|
1452
2053
|
const hitfaces = this.stickerDat.faces;
|
|
1453
2054
|
this.movingObj = obj2;
|
|
@@ -1459,7 +2060,7 @@ var PG3D = class extends Object3D2 {
|
|
|
1459
2060
|
stickerDat.textureMapper,
|
|
1460
2061
|
this.stickerDat
|
|
1461
2062
|
);
|
|
1462
|
-
facedef.cubie.scale.set(
|
|
2063
|
+
facedef.cubie.scale.set(PG_SCALE2, PG_SCALE2, PG_SCALE2);
|
|
1463
2064
|
this.add(facedef.cubie);
|
|
1464
2065
|
this.controlTargets.push(facedef.cubie.children[0]);
|
|
1465
2066
|
}
|
|
@@ -1537,7 +2138,7 @@ var PG3D = class extends Object3D2 {
|
|
|
1537
2138
|
}
|
|
1538
2139
|
}
|
|
1539
2140
|
for (const axis of this.stickerDat.axis) {
|
|
1540
|
-
const product = point.dot(new
|
|
2141
|
+
const product = point.dot(new Vector35(...axis.coordinates));
|
|
1541
2142
|
if (product > closestMoveDotProduct) {
|
|
1542
2143
|
const modified = this.stickerDat.notationMapper.notationToExternal(
|
|
1543
2144
|
modify(axis.quantumMove)
|
|
@@ -1771,7 +2372,7 @@ var PG3D = class extends Object3D2 {
|
|
|
1771
2372
|
} else if (v === 1) {
|
|
1772
2373
|
this.hintMaterial = this.stickerMaterial;
|
|
1773
2374
|
} else {
|
|
1774
|
-
this.hintMaterial = new
|
|
2375
|
+
this.hintMaterial = new MeshBasicMaterial3({
|
|
1775
2376
|
vertexColors: true,
|
|
1776
2377
|
transparent: true,
|
|
1777
2378
|
opacity: v
|
|
@@ -1819,7 +2420,7 @@ var PG3D = class extends Object3D2 {
|
|
|
1819
2420
|
}
|
|
1820
2421
|
filler.uvs.copyWithin(6 * filler.sz, 0, 6 * filler.sz);
|
|
1821
2422
|
const sa1 = filler.uvs.subarray(0, 6 * filler.sz);
|
|
1822
|
-
this.fixedGeo.setAttribute("uv", new
|
|
2423
|
+
this.fixedGeo.setAttribute("uv", new BufferAttribute4(sa1, 2, true));
|
|
1823
2424
|
}
|
|
1824
2425
|
experimentalUpdateTexture(enabled, stickerTexture, hintTexture) {
|
|
1825
2426
|
if (!stickerTexture) {
|
|
@@ -1834,7 +2435,7 @@ var PG3D = class extends Object3D2 {
|
|
|
1834
2435
|
this.stickerMaterialDisposable = false;
|
|
1835
2436
|
}
|
|
1836
2437
|
if (enabled) {
|
|
1837
|
-
this.stickerMaterial = new
|
|
2438
|
+
this.stickerMaterial = new MeshBasicMaterial3({
|
|
1838
2439
|
map: stickerTexture,
|
|
1839
2440
|
side: FrontSide2,
|
|
1840
2441
|
transparent: false
|
|
@@ -1848,7 +2449,7 @@ var PG3D = class extends Object3D2 {
|
|
|
1848
2449
|
this.hintMaterialDisposable = false;
|
|
1849
2450
|
}
|
|
1850
2451
|
if (enabled) {
|
|
1851
|
-
this.hintMaterial = new
|
|
2452
|
+
this.hintMaterial = new MeshBasicMaterial3({
|
|
1852
2453
|
map: hintTexture,
|
|
1853
2454
|
side: FrontSide2,
|
|
1854
2455
|
transparent: true
|
|
@@ -1869,73 +2470,685 @@ var PG3D = class extends Object3D2 {
|
|
|
1869
2470
|
}
|
|
1870
2471
|
};
|
|
1871
2472
|
|
|
1872
|
-
// src/cubing/twisty/views/3D/puzzles/
|
|
1873
|
-
import {
|
|
1874
|
-
import {
|
|
1875
|
-
import {
|
|
1876
|
-
import {
|
|
1877
|
-
import {
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
import { Vector3 as
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
var
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
0,
|
|
1901
|
-
Math.cos(15 * DEGREE)
|
|
1902
|
-
).normalize();
|
|
1903
|
-
var SLICE_QUATERNION = new Quaternion2().setFromAxisAngle(SLICE_AXIS, TAU / 2);
|
|
1904
|
-
var HALF_TURN_Y = new Quaternion2().setFromAxisAngle(Y_AXIS, TAU / 2);
|
|
1905
|
-
function slotAzimuthDegrees(slot) {
|
|
1906
|
-
return FRAME_OFFSET_DEGREES + (slot < WEDGES_PER_LAYER ? SLOT_ANGLE_DEGREES * slot + 120 : 90 - SLOT_ANGLE_DEGREES * (slot - WEDGES_PER_LAYER));
|
|
2473
|
+
// src/cubing/twisty/views/3D/puzzles/SolidPieces.ts
|
|
2474
|
+
import { BufferAttribute as BufferAttribute5 } from "three/src/core/BufferAttribute.js";
|
|
2475
|
+
import { BufferGeometry as BufferGeometry5 } from "three/src/core/BufferGeometry.js";
|
|
2476
|
+
import { Color as Color4 } from "three/src/math/Color.js";
|
|
2477
|
+
import { Matrix4 as Matrix43 } from "three/src/math/Matrix4.js";
|
|
2478
|
+
import { Vector3 as Vector37 } from "three/src/math/Vector3.js";
|
|
2479
|
+
|
|
2480
|
+
// src/cubing/twisty/views/3D/puzzles/SolidPieceGeometry.ts
|
|
2481
|
+
import { Vector3 as Vector36 } from "three/src/math/Vector3.js";
|
|
2482
|
+
var SOLID_PIECE_GROOVE = 0.018;
|
|
2483
|
+
var SOLID_PIECE_CHAMFER = 5e-3;
|
|
2484
|
+
var BOX_PLANE = -1;
|
|
2485
|
+
function boxFacet(planeIdx, normal2, size) {
|
|
2486
|
+
const reference = Math.abs(normal2.y) > 0.5 ? new Vector36(0, 0, 1) : new Vector36(0, 1, 0);
|
|
2487
|
+
const u = new Vector36().crossVectors(reference, normal2).normalize();
|
|
2488
|
+
const v = new Vector36().crossVectors(normal2, u);
|
|
2489
|
+
const center = normal2.clone().multiplyScalar(size);
|
|
2490
|
+
return {
|
|
2491
|
+
plane: planeIdx,
|
|
2492
|
+
vertices: [
|
|
2493
|
+
[-1, -1],
|
|
2494
|
+
[1, -1],
|
|
2495
|
+
[1, 1],
|
|
2496
|
+
[-1, 1]
|
|
2497
|
+
].map(
|
|
2498
|
+
([su, sv]) => center.clone().addScaledVector(u, su * size).addScaledVector(v, sv * size)
|
|
2499
|
+
)
|
|
2500
|
+
};
|
|
1907
2501
|
}
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
)
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
2502
|
+
var BOX_NORMALS = [
|
|
2503
|
+
new Vector36(1, 0, 0),
|
|
2504
|
+
new Vector36(-1, 0, 0),
|
|
2505
|
+
new Vector36(0, 1, 0),
|
|
2506
|
+
new Vector36(0, -1, 0),
|
|
2507
|
+
new Vector36(0, 0, 1),
|
|
2508
|
+
new Vector36(0, 0, -1)
|
|
2509
|
+
];
|
|
2510
|
+
function orderInPlane(points, normal2) {
|
|
2511
|
+
const centroid = new Vector36();
|
|
2512
|
+
for (const point of points) {
|
|
2513
|
+
centroid.addScaledVector(point, 1 / points.length);
|
|
1917
2514
|
}
|
|
1918
|
-
|
|
2515
|
+
const reference = new Vector36().subVectors(points[0], centroid).normalize();
|
|
2516
|
+
const other = new Vector36().crossVectors(normal2, reference);
|
|
2517
|
+
const offset = new Vector36();
|
|
2518
|
+
return points.map((point) => {
|
|
2519
|
+
offset.subVectors(point, centroid);
|
|
2520
|
+
return {
|
|
2521
|
+
point,
|
|
2522
|
+
angle: Math.atan2(offset.dot(other), offset.dot(reference))
|
|
2523
|
+
};
|
|
2524
|
+
}).sort((a, b) => a.angle - b.angle).map(({ point }) => point);
|
|
1919
2525
|
}
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
const phase = homeSlot < WEDGES_PER_LAYER ? homeSlot % 3 : (homeSlot - WEDGES_PER_LAYER + 2) % 3;
|
|
1923
|
-
return phase === 0 ? "cornerHigh" : phase === 1 ? "cornerLow" : "edge";
|
|
2526
|
+
function vertexKey(vertex, quantum) {
|
|
2527
|
+
return `${Math.round(vertex.x / quantum)},${Math.round(vertex.y / quantum)},${Math.round(vertex.z / quantum)}`;
|
|
1924
2528
|
}
|
|
1925
|
-
function
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
2529
|
+
function clipByPlane(facets, plane, planeIdx, epsilon) {
|
|
2530
|
+
const clipped = [];
|
|
2531
|
+
const capPoints = [];
|
|
2532
|
+
const capKeys = /* @__PURE__ */ new Set();
|
|
2533
|
+
const quantum = epsilon * 10;
|
|
2534
|
+
for (const facet of facets) {
|
|
2535
|
+
const kept = [];
|
|
2536
|
+
const count = facet.vertices.length;
|
|
2537
|
+
for (let i = 0; i < count; i++) {
|
|
2538
|
+
const current = facet.vertices[i];
|
|
2539
|
+
const next = facet.vertices[(i + 1) % count];
|
|
2540
|
+
const currentSide = current.dot(plane.normal) - plane.offset;
|
|
2541
|
+
const nextSide = next.dot(plane.normal) - plane.offset;
|
|
2542
|
+
if (currentSide <= epsilon) {
|
|
2543
|
+
kept.push(current);
|
|
2544
|
+
}
|
|
2545
|
+
if (currentSide > epsilon && nextSide < -epsilon || currentSide < -epsilon && nextSide > epsilon) {
|
|
2546
|
+
kept.push(
|
|
2547
|
+
current.clone().lerp(next, currentSide / (currentSide - nextSide))
|
|
2548
|
+
);
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
if (kept.length < 3) {
|
|
2552
|
+
continue;
|
|
2553
|
+
}
|
|
2554
|
+
clipped.push({ plane: facet.plane, vertices: kept });
|
|
2555
|
+
for (const vertex of kept) {
|
|
2556
|
+
if (Math.abs(vertex.dot(plane.normal) - plane.offset) > epsilon) {
|
|
2557
|
+
continue;
|
|
2558
|
+
}
|
|
2559
|
+
const key = vertexKey(vertex, quantum);
|
|
2560
|
+
if (!capKeys.has(key)) {
|
|
2561
|
+
capKeys.add(key);
|
|
2562
|
+
capPoints.push(vertex);
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
1933
2565
|
}
|
|
2566
|
+
if (capPoints.length >= 3) {
|
|
2567
|
+
clipped.push({
|
|
2568
|
+
plane: planeIdx,
|
|
2569
|
+
vertices: orderInPlane(capPoints, plane.normal)
|
|
2570
|
+
});
|
|
2571
|
+
}
|
|
2572
|
+
return clipped;
|
|
1934
2573
|
}
|
|
1935
|
-
function
|
|
1936
|
-
|
|
2574
|
+
function buildFacets(planes, radius, epsilon) {
|
|
2575
|
+
let facets = BOX_NORMALS.map(
|
|
2576
|
+
(normal2) => boxFacet(BOX_PLANE, normal2, radius * 4)
|
|
2577
|
+
);
|
|
2578
|
+
for (let planeIdx = 0; planeIdx < planes.length; planeIdx++) {
|
|
2579
|
+
facets = clipByPlane(facets, planes[planeIdx], planeIdx, epsilon);
|
|
2580
|
+
}
|
|
2581
|
+
if (facets.some((facet) => facet.plane === BOX_PLANE) || facets.length < 4) {
|
|
2582
|
+
return null;
|
|
2583
|
+
}
|
|
2584
|
+
return facets;
|
|
1937
2585
|
}
|
|
1938
|
-
function
|
|
2586
|
+
function chamferPlanes(facets, planes, chamfer, epsilon) {
|
|
2587
|
+
const quantum = epsilon * 10;
|
|
2588
|
+
const edges = /* @__PURE__ */ new Map();
|
|
2589
|
+
for (const facet of facets) {
|
|
2590
|
+
const { vertices } = facet;
|
|
2591
|
+
for (let i = 0; i < vertices.length; i++) {
|
|
2592
|
+
const edgeKey = [
|
|
2593
|
+
vertexKey(vertices[i], quantum),
|
|
2594
|
+
vertexKey(vertices[(i + 1) % vertices.length], quantum)
|
|
2595
|
+
].sort().join("|");
|
|
2596
|
+
const sharing = edges.get(edgeKey);
|
|
2597
|
+
if (sharing) {
|
|
2598
|
+
sharing.push(facet.plane);
|
|
2599
|
+
} else {
|
|
2600
|
+
edges.set(edgeKey, [facet.plane]);
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
const added = [];
|
|
2605
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2606
|
+
for (const sharing of edges.values()) {
|
|
2607
|
+
if (sharing.length !== 2) {
|
|
2608
|
+
continue;
|
|
2609
|
+
}
|
|
2610
|
+
const [a, b] = sharing;
|
|
2611
|
+
const colored = planes[a].source !== -1 ? a : b;
|
|
2612
|
+
const internal = planes[a].source !== -1 ? b : a;
|
|
2613
|
+
if (planes[colored].source === -1 || planes[internal].source !== -1) {
|
|
2614
|
+
continue;
|
|
2615
|
+
}
|
|
2616
|
+
const pairKey = `${colored}|${internal}`;
|
|
2617
|
+
if (seen.has(pairKey)) {
|
|
2618
|
+
continue;
|
|
2619
|
+
}
|
|
2620
|
+
seen.add(pairKey);
|
|
2621
|
+
const sum = new Vector36().addVectors(
|
|
2622
|
+
planes[colored].normal,
|
|
2623
|
+
planes[internal].normal
|
|
2624
|
+
);
|
|
2625
|
+
if (sum.length() < 1e-6) {
|
|
2626
|
+
continue;
|
|
2627
|
+
}
|
|
2628
|
+
added.push({
|
|
2629
|
+
normal: sum.clone().normalize(),
|
|
2630
|
+
// Where the two faces meet, measured along the bevel's own direction.
|
|
2631
|
+
offset: (planes[colored].offset + planes[internal].offset) / sum.length() - chamfer,
|
|
2632
|
+
source: planes[colored].source
|
|
2633
|
+
});
|
|
2634
|
+
}
|
|
2635
|
+
return added;
|
|
2636
|
+
}
|
|
2637
|
+
function solidPieceGeometry(planes, options) {
|
|
2638
|
+
const epsilon = options.radius * 1e-6;
|
|
2639
|
+
const working = planes.map((plane, index) => ({
|
|
2640
|
+
normal: plane.normal,
|
|
2641
|
+
offset: plane.offset - (plane.color === null ? options.groove : 0),
|
|
2642
|
+
source: plane.color === null ? -1 : index
|
|
2643
|
+
}));
|
|
2644
|
+
const plain = buildFacets(working, options.radius, epsilon);
|
|
2645
|
+
if (!plain) {
|
|
2646
|
+
return null;
|
|
2647
|
+
}
|
|
2648
|
+
const withChamfers = options.chamfer > 0 ? working.concat(chamferPlanes(plain, working, options.chamfer, epsilon)) : working;
|
|
2649
|
+
const facets = (options.chamfer > 0 ? buildFacets(withChamfers, options.radius, epsilon) : plain) ?? plain;
|
|
2650
|
+
const positions = [];
|
|
2651
|
+
const ranges = planes.map(() => []);
|
|
2652
|
+
const internalRanges = [];
|
|
2653
|
+
for (const facet of facets) {
|
|
2654
|
+
const start = positions.length / 3;
|
|
2655
|
+
for (let i = 1; i < facet.vertices.length - 1; i++) {
|
|
2656
|
+
for (const vertex of [
|
|
2657
|
+
facet.vertices[0],
|
|
2658
|
+
facet.vertices[i],
|
|
2659
|
+
facet.vertices[i + 1]
|
|
2660
|
+
]) {
|
|
2661
|
+
positions.push(vertex.x, vertex.y, vertex.z);
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
const range = { start, count: positions.length / 3 - start };
|
|
2665
|
+
if (range.count === 0) {
|
|
2666
|
+
continue;
|
|
2667
|
+
}
|
|
2668
|
+
if (withChamfers[facet.plane].source === -1) {
|
|
2669
|
+
internalRanges.push(range);
|
|
2670
|
+
} else {
|
|
2671
|
+
ranges[withChamfers[facet.plane].source].push(range);
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
return {
|
|
2675
|
+
positions: new Float32Array(positions),
|
|
2676
|
+
vertexCount: positions.length / 3,
|
|
2677
|
+
ranges,
|
|
2678
|
+
internalRanges
|
|
2679
|
+
};
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
// src/cubing/twisty/views/3D/puzzles/SolidPieces.ts
|
|
2683
|
+
var PG_SCALE3 = 0.5;
|
|
2684
|
+
var EPSILON = 1e-6;
|
|
2685
|
+
function vertexAt(sticker, index) {
|
|
2686
|
+
return new Vector37(
|
|
2687
|
+
sticker.coords[3 * index],
|
|
2688
|
+
sticker.coords[3 * index + 1],
|
|
2689
|
+
sticker.coords[3 * index + 2]
|
|
2690
|
+
);
|
|
2691
|
+
}
|
|
2692
|
+
function polygonOf(sticker) {
|
|
2693
|
+
const vertices = [];
|
|
2694
|
+
for (let i = 0; i < sticker.coords.length / 3; i++) {
|
|
2695
|
+
vertices.push(vertexAt(sticker, i));
|
|
2696
|
+
}
|
|
2697
|
+
return vertices;
|
|
2698
|
+
}
|
|
2699
|
+
function outwardNormal(polygon) {
|
|
2700
|
+
const normal2 = new Vector37().crossVectors(
|
|
2701
|
+
new Vector37().subVectors(polygon[1], polygon[0]),
|
|
2702
|
+
new Vector37().subVectors(polygon[2], polygon[0])
|
|
2703
|
+
);
|
|
2704
|
+
if (normal2.length() < EPSILON) {
|
|
2705
|
+
return null;
|
|
2706
|
+
}
|
|
2707
|
+
normal2.normalize();
|
|
2708
|
+
return normal2.dot(polygon[0]) < 0 ? normal2.negate() : normal2;
|
|
2709
|
+
}
|
|
2710
|
+
function keyOf(vertex, quantum) {
|
|
2711
|
+
return `${Math.round(vertex.x / quantum)},${Math.round(vertex.y / quantum)},${Math.round(vertex.z / quantum)}`;
|
|
2712
|
+
}
|
|
2713
|
+
function faceStyles(faceNormals, faceColors) {
|
|
2714
|
+
const styles = [];
|
|
2715
|
+
const cubeFaces = [];
|
|
2716
|
+
for (let face2 = 0; face2 < faceNormals.length; face2++) {
|
|
2717
|
+
const normal2 = faceNormals[face2];
|
|
2718
|
+
const color = faceColors[face2];
|
|
2719
|
+
if (!normal2 || color === void 0) {
|
|
2720
|
+
return null;
|
|
2721
|
+
}
|
|
2722
|
+
cubeFaces.push(
|
|
2723
|
+
cubeFaceStyles.findIndex((style) => style.vector.dot(normal2) > 0.999)
|
|
2724
|
+
);
|
|
2725
|
+
}
|
|
2726
|
+
const isCube = faceNormals.length === cubeFaceStyles.length && new Set(cubeFaces).size === cubeFaceStyles.length && !cubeFaces.includes(-1) && new Set(faceColors).size === faceColors.length;
|
|
2727
|
+
for (let face2 = 0; face2 < faceNormals.length; face2++) {
|
|
2728
|
+
if (isCube) {
|
|
2729
|
+
styles.push(cubeFaceStyles[cubeFaces[face2]]);
|
|
2730
|
+
continue;
|
|
2731
|
+
}
|
|
2732
|
+
const color = new Color4(faceColors[face2]).getHex();
|
|
2733
|
+
const dimColor = new Color4(color).multiplyScalar(0.5).getHex();
|
|
2734
|
+
styles.push({
|
|
2735
|
+
color,
|
|
2736
|
+
dimColor,
|
|
2737
|
+
hintColor: color,
|
|
2738
|
+
hintDimColor: dimColor,
|
|
2739
|
+
hintOpacityScale: 1
|
|
2740
|
+
});
|
|
2741
|
+
}
|
|
2742
|
+
return styles;
|
|
2743
|
+
}
|
|
2744
|
+
function hintPolygon(polygon, normal2) {
|
|
2745
|
+
const centroid = new Vector37();
|
|
2746
|
+
for (const vertex of polygon) {
|
|
2747
|
+
centroid.addScaledVector(vertex, 1 / polygon.length);
|
|
2748
|
+
}
|
|
2749
|
+
return polygon.map(
|
|
2750
|
+
(vertex) => centroid.clone().addScaledVector(
|
|
2751
|
+
new Vector37().subVectors(vertex, centroid),
|
|
2752
|
+
HINT_FACELET_SCALE
|
|
2753
|
+
).addScaledVector(normal2, HINT_FACELET_ELEVATION)
|
|
2754
|
+
);
|
|
2755
|
+
}
|
|
2756
|
+
function solidPuzzlePlan(stickerDat) {
|
|
2757
|
+
const { stickers } = stickerDat;
|
|
2758
|
+
if (stickers.length === 0 || stickerDat.faces.length === 0) {
|
|
2759
|
+
return null;
|
|
2760
|
+
}
|
|
2761
|
+
let radius = 0;
|
|
2762
|
+
let faceletArea = 0;
|
|
2763
|
+
let faceletCount = 0;
|
|
2764
|
+
for (const sticker of stickers) {
|
|
2765
|
+
if (sticker.coords.length < 9 || sticker.coords.length % 3 !== 0) {
|
|
2766
|
+
return null;
|
|
2767
|
+
}
|
|
2768
|
+
for (let i = 0; i < sticker.coords.length; i += 3) {
|
|
2769
|
+
radius = Math.max(
|
|
2770
|
+
radius,
|
|
2771
|
+
Math.hypot(
|
|
2772
|
+
sticker.coords[i],
|
|
2773
|
+
sticker.coords[i + 1],
|
|
2774
|
+
sticker.coords[i + 2]
|
|
2775
|
+
)
|
|
2776
|
+
);
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
const quantum = radius * 1e-5;
|
|
2780
|
+
const faceNormals = new Array(stickerDat.faces.length);
|
|
2781
|
+
const faceColors = new Array(stickerDat.faces.length);
|
|
2782
|
+
const pieces = /* @__PURE__ */ new Map();
|
|
2783
|
+
const order = [];
|
|
2784
|
+
const edges = /* @__PURE__ */ new Map();
|
|
2785
|
+
let faceDistance = 0;
|
|
2786
|
+
for (const sticker of stickers) {
|
|
2787
|
+
const pieceKey = `${sticker.orbit}/${sticker.ord}`;
|
|
2788
|
+
let piece = pieces.get(pieceKey);
|
|
2789
|
+
if (!piece) {
|
|
2790
|
+
piece = {
|
|
2791
|
+
orbit: sticker.orbit,
|
|
2792
|
+
ord: sticker.ord,
|
|
2793
|
+
stickers: [],
|
|
2794
|
+
duplicates: []
|
|
2795
|
+
};
|
|
2796
|
+
pieces.set(pieceKey, piece);
|
|
2797
|
+
order.push(piece);
|
|
2798
|
+
}
|
|
2799
|
+
if (sticker.isDup) {
|
|
2800
|
+
piece.duplicates.push(sticker);
|
|
2801
|
+
continue;
|
|
2802
|
+
}
|
|
2803
|
+
const polygon = polygonOf(sticker);
|
|
2804
|
+
const normal2 = outwardNormal(polygon);
|
|
2805
|
+
if (!normal2) {
|
|
2806
|
+
return null;
|
|
2807
|
+
}
|
|
2808
|
+
for (const vertex of polygon) {
|
|
2809
|
+
if (Math.abs(vertex.dot(normal2) - polygon[0].dot(normal2)) > quantum) {
|
|
2810
|
+
return null;
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
const distance = polygon[0].dot(normal2);
|
|
2814
|
+
faceDistance = faceDistance || distance;
|
|
2815
|
+
if (faceNormals[sticker.face]) {
|
|
2816
|
+
if (faceNormals[sticker.face].dot(normal2) < 0.999) {
|
|
2817
|
+
return null;
|
|
2818
|
+
}
|
|
2819
|
+
} else {
|
|
2820
|
+
faceNormals[sticker.face] = normal2;
|
|
2821
|
+
}
|
|
2822
|
+
faceColors[sticker.face] ??= sticker.color;
|
|
2823
|
+
if (faceColors[sticker.face] !== sticker.color) {
|
|
2824
|
+
return null;
|
|
2825
|
+
}
|
|
2826
|
+
faceletCount++;
|
|
2827
|
+
for (let i = 1; i < polygon.length - 1; i++) {
|
|
2828
|
+
faceletArea += new Vector37().subVectors(polygon[i], polygon[0]).cross(new Vector37().subVectors(polygon[i + 1], polygon[0])).length() / 2;
|
|
2829
|
+
}
|
|
2830
|
+
piece.stickers.push({ sticker, polygon });
|
|
2831
|
+
for (let i = 0; i < polygon.length; i++) {
|
|
2832
|
+
const edgeKey = [
|
|
2833
|
+
keyOf(polygon[i], quantum),
|
|
2834
|
+
keyOf(polygon[(i + 1) % polygon.length], quantum)
|
|
2835
|
+
].sort().join("|");
|
|
2836
|
+
const sharing = edges.get(edgeKey);
|
|
2837
|
+
if (sharing) {
|
|
2838
|
+
sharing.add(pieceKey);
|
|
2839
|
+
} else {
|
|
2840
|
+
edges.set(edgeKey, /* @__PURE__ */ new Set([pieceKey]));
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
const styles = faceStyles(faceNormals, faceColors);
|
|
2845
|
+
if (!styles) {
|
|
2846
|
+
return null;
|
|
2847
|
+
}
|
|
2848
|
+
const featureScale = Math.min(
|
|
2849
|
+
faceDistance,
|
|
2850
|
+
1.5 * Math.sqrt(faceletArea / faceletCount)
|
|
2851
|
+
);
|
|
2852
|
+
const cuts = cutPlanes(
|
|
2853
|
+
order,
|
|
2854
|
+
edges,
|
|
2855
|
+
stickerDat.axis.map((axis) => new Vector37(...axis.coordinates).normalize()),
|
|
2856
|
+
quantum
|
|
2857
|
+
);
|
|
2858
|
+
if (!cuts) {
|
|
2859
|
+
return null;
|
|
2860
|
+
}
|
|
2861
|
+
const plans = [];
|
|
2862
|
+
for (const piece of order) {
|
|
2863
|
+
const plan = newSolidPiece(
|
|
2864
|
+
piece,
|
|
2865
|
+
{ planes: cuts, edges },
|
|
2866
|
+
{
|
|
2867
|
+
quantum,
|
|
2868
|
+
radius,
|
|
2869
|
+
featureScale
|
|
2870
|
+
}
|
|
2871
|
+
);
|
|
2872
|
+
if (!plan) {
|
|
2873
|
+
return null;
|
|
2874
|
+
}
|
|
2875
|
+
plans.push(plan);
|
|
2876
|
+
}
|
|
2877
|
+
return { faceStyles: styles, pieces: plans, scale: PG_SCALE3 };
|
|
2878
|
+
}
|
|
2879
|
+
function cutPlanes(pieces, edges, axes, quantum) {
|
|
2880
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
2881
|
+
const boundaryEdges = [];
|
|
2882
|
+
for (const piece of pieces) {
|
|
2883
|
+
const pieceKey = `${piece.orbit}/${piece.ord}`;
|
|
2884
|
+
for (const { polygon } of piece.stickers) {
|
|
2885
|
+
for (let i = 0; i < polygon.length; i++) {
|
|
2886
|
+
const a = polygon[i];
|
|
2887
|
+
const b = polygon[(i + 1) % polygon.length];
|
|
2888
|
+
const sharing = edges.get(
|
|
2889
|
+
[keyOf(a, quantum), keyOf(b, quantum)].sort().join("|")
|
|
2890
|
+
);
|
|
2891
|
+
if (!sharing || ![...sharing].some((key) => key !== pieceKey)) {
|
|
2892
|
+
continue;
|
|
2893
|
+
}
|
|
2894
|
+
boundaryEdges.push([a, b]);
|
|
2895
|
+
for (const axis of axes) {
|
|
2896
|
+
if (Math.abs(a.dot(axis) - b.dot(axis)) > quantum) {
|
|
2897
|
+
continue;
|
|
2898
|
+
}
|
|
2899
|
+
const offset = a.dot(axis);
|
|
2900
|
+
const key = `${keyOf(axis, 1e-4)}|${Math.round(offset / quantum)}`;
|
|
2901
|
+
const candidate = candidates.get(key);
|
|
2902
|
+
if (candidate) {
|
|
2903
|
+
candidate.uses++;
|
|
2904
|
+
} else {
|
|
2905
|
+
candidates.set(key, { normal: axis, offset, uses: 1 });
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
const cuts = [...candidates.values()].sort((a, b) => b.uses - a.uses);
|
|
2912
|
+
const kept = cuts.filter(({ normal: normal2, offset }) => {
|
|
2913
|
+
for (const piece of pieces) {
|
|
2914
|
+
let above = false;
|
|
2915
|
+
let below = false;
|
|
2916
|
+
for (const { polygon } of piece.stickers) {
|
|
2917
|
+
let onIt = true;
|
|
2918
|
+
for (const vertex of polygon) {
|
|
2919
|
+
above ||= vertex.dot(normal2) > offset + quantum;
|
|
2920
|
+
below ||= vertex.dot(normal2) < offset - quantum;
|
|
2921
|
+
onIt &&= Math.abs(vertex.dot(normal2) - offset) <= quantum;
|
|
2922
|
+
}
|
|
2923
|
+
if (onIt) {
|
|
2924
|
+
return false;
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
if (above && below) {
|
|
2928
|
+
return false;
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
return true;
|
|
2932
|
+
});
|
|
2933
|
+
for (const [a, b] of boundaryEdges) {
|
|
2934
|
+
const covered = kept.some(
|
|
2935
|
+
({ normal: normal2, offset }) => Math.abs(a.dot(normal2) - offset) < quantum && Math.abs(b.dot(normal2) - offset) < quantum
|
|
2936
|
+
);
|
|
2937
|
+
if (!covered) {
|
|
2938
|
+
return null;
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
return kept;
|
|
2942
|
+
}
|
|
2943
|
+
function newSolidPiece(piece, cuts, sizes) {
|
|
2944
|
+
if (piece.stickers.length === 0) {
|
|
2945
|
+
return null;
|
|
2946
|
+
}
|
|
2947
|
+
const planes = [];
|
|
2948
|
+
const planeOfSticker = /* @__PURE__ */ new Map();
|
|
2949
|
+
const interior = new Vector37();
|
|
2950
|
+
for (const { polygon } of piece.stickers) {
|
|
2951
|
+
for (const vertex of polygon) {
|
|
2952
|
+
interior.addScaledVector(
|
|
2953
|
+
vertex,
|
|
2954
|
+
1 / polygon.length / piece.stickers.length
|
|
2955
|
+
);
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
for (const { sticker, polygon } of piece.stickers) {
|
|
2959
|
+
const normal2 = outwardNormal(polygon);
|
|
2960
|
+
planeOfSticker.set(sticker, planes.length);
|
|
2961
|
+
planes.push({
|
|
2962
|
+
normal: normal2,
|
|
2963
|
+
offset: polygon[0].dot(normal2),
|
|
2964
|
+
color: sticker.face
|
|
2965
|
+
});
|
|
2966
|
+
}
|
|
2967
|
+
const pieceKey = `${piece.orbit}/${piece.ord}`;
|
|
2968
|
+
for (const { polygon } of piece.stickers) {
|
|
2969
|
+
for (let i = 0; i < polygon.length; i++) {
|
|
2970
|
+
const a = polygon[i];
|
|
2971
|
+
const b = polygon[(i + 1) % polygon.length];
|
|
2972
|
+
const sharing = cuts.edges.get(
|
|
2973
|
+
[keyOf(a, sizes.quantum), keyOf(b, sizes.quantum)].sort().join("|")
|
|
2974
|
+
);
|
|
2975
|
+
if (!sharing || ![...sharing].some((key) => key !== pieceKey)) {
|
|
2976
|
+
continue;
|
|
2977
|
+
}
|
|
2978
|
+
const cut = cuts.planes.find(
|
|
2979
|
+
({ normal: normal3, offset: offset2 }) => Math.abs(a.dot(normal3) - offset2) < sizes.quantum && Math.abs(b.dot(normal3) - offset2) < sizes.quantum
|
|
2980
|
+
);
|
|
2981
|
+
if (!cut) {
|
|
2982
|
+
return null;
|
|
2983
|
+
}
|
|
2984
|
+
const outward = interior.dot(cut.normal) < cut.offset;
|
|
2985
|
+
const normal2 = outward ? cut.normal.clone() : cut.normal.clone().negate();
|
|
2986
|
+
const offset = outward ? cut.offset : -cut.offset;
|
|
2987
|
+
if (planes.some(
|
|
2988
|
+
(plane) => plane.color === null && plane.normal.dot(normal2) > 0.9999 && Math.abs(plane.offset - offset) < sizes.quantum
|
|
2989
|
+
)) {
|
|
2990
|
+
continue;
|
|
2991
|
+
}
|
|
2992
|
+
planes.push({ normal: normal2, offset, color: null });
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
for (const cut of cuts.planes) {
|
|
2996
|
+
let touches = false;
|
|
2997
|
+
let above = false;
|
|
2998
|
+
let below = false;
|
|
2999
|
+
for (const { polygon } of piece.stickers) {
|
|
3000
|
+
for (const vertex of polygon) {
|
|
3001
|
+
const side = vertex.dot(cut.normal) - cut.offset;
|
|
3002
|
+
if (Math.abs(side) <= sizes.quantum) {
|
|
3003
|
+
touches = true;
|
|
3004
|
+
} else if (side > 0) {
|
|
3005
|
+
above = true;
|
|
3006
|
+
} else {
|
|
3007
|
+
below = true;
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
if (touches || above === below) {
|
|
3012
|
+
continue;
|
|
3013
|
+
}
|
|
3014
|
+
planes.push(
|
|
3015
|
+
above ? {
|
|
3016
|
+
normal: cut.normal.clone().negate(),
|
|
3017
|
+
offset: -cut.offset,
|
|
3018
|
+
color: null
|
|
3019
|
+
} : { normal: cut.normal.clone(), offset: cut.offset, color: null }
|
|
3020
|
+
);
|
|
3021
|
+
}
|
|
3022
|
+
const solid = solidPieceGeometry(planes, {
|
|
3023
|
+
groove: SOLID_PIECE_GROOVE * sizes.featureScale,
|
|
3024
|
+
chamfer: SOLID_PIECE_CHAMFER * sizes.featureScale,
|
|
3025
|
+
radius: sizes.radius
|
|
3026
|
+
});
|
|
3027
|
+
if (!solid) {
|
|
3028
|
+
return null;
|
|
3029
|
+
}
|
|
3030
|
+
const hintPositions = [];
|
|
3031
|
+
const hintRanges = /* @__PURE__ */ new Map();
|
|
3032
|
+
for (const { sticker, polygon } of piece.stickers) {
|
|
3033
|
+
const normal2 = outwardNormal(polygon);
|
|
3034
|
+
const hint2 = hintPolygon(polygon, normal2);
|
|
3035
|
+
const start = hintPositions.length / 3;
|
|
3036
|
+
for (let i = 1; i < hint2.length - 1; i++) {
|
|
3037
|
+
for (const vertex of [hint2[0], hint2[i], hint2[i + 1]]) {
|
|
3038
|
+
hintPositions.push(vertex.x, vertex.y, vertex.z);
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
hintRanges.set(sticker, {
|
|
3042
|
+
start,
|
|
3043
|
+
count: hintPositions.length / 3 - start
|
|
3044
|
+
});
|
|
3045
|
+
}
|
|
3046
|
+
const body = newPieceMesh2(solid.positions);
|
|
3047
|
+
const hint = hintPositions.length > 0 ? newPieceMesh2(new Float32Array(hintPositions)) : null;
|
|
3048
|
+
paintInternal(body.colors, solid.internalRanges);
|
|
3049
|
+
const facelets = piece.stickers.map(({ sticker }) => ({
|
|
3050
|
+
ori: sticker.ori,
|
|
3051
|
+
faceStyle: sticker.face,
|
|
3052
|
+
body: solid.ranges[planeOfSticker.get(sticker)],
|
|
3053
|
+
hint: [hintRanges.get(sticker)]
|
|
3054
|
+
}));
|
|
3055
|
+
for (const sticker of piece.duplicates) {
|
|
3056
|
+
facelets.push({
|
|
3057
|
+
ori: sticker.ori,
|
|
3058
|
+
faceStyle: sticker.face,
|
|
3059
|
+
body: [],
|
|
3060
|
+
hint: []
|
|
3061
|
+
});
|
|
3062
|
+
}
|
|
3063
|
+
return {
|
|
3064
|
+
orbit: piece.orbit,
|
|
3065
|
+
ord: piece.ord,
|
|
3066
|
+
// The geometry is already where the piece belongs.
|
|
3067
|
+
home: new Matrix43(),
|
|
3068
|
+
body,
|
|
3069
|
+
hint,
|
|
3070
|
+
facelets
|
|
3071
|
+
};
|
|
3072
|
+
}
|
|
3073
|
+
function newPieceMesh2(positions) {
|
|
3074
|
+
const geometry = new BufferGeometry5();
|
|
3075
|
+
geometry.setAttribute("position", new BufferAttribute5(positions, 3));
|
|
3076
|
+
const colors = new BufferAttribute5(
|
|
3077
|
+
new Uint8Array(4 * positions.length / 3),
|
|
3078
|
+
4,
|
|
3079
|
+
true
|
|
3080
|
+
);
|
|
3081
|
+
geometry.setAttribute("color", colors);
|
|
3082
|
+
return { geometry, colors };
|
|
3083
|
+
}
|
|
3084
|
+
|
|
3085
|
+
// src/cubing/twisty/views/3D/puzzles/Square1_3D.ts
|
|
3086
|
+
import { DoubleSide as DoubleSide3 } from "three/src/constants.js";
|
|
3087
|
+
import { BufferAttribute as BufferAttribute6 } from "three/src/core/BufferAttribute.js";
|
|
3088
|
+
import { BufferGeometry as BufferGeometry6 } from "three/src/core/BufferGeometry.js";
|
|
3089
|
+
import { Object3D as Object3D3 } from "three/src/core/Object3D.js";
|
|
3090
|
+
import { MeshBasicMaterial as MeshBasicMaterial4 } from "three/src/materials/MeshBasicMaterial.js";
|
|
3091
|
+
import { Color as Color5 } from "three/src/math/Color.js";
|
|
3092
|
+
import { Quaternion as Quaternion2 } from "three/src/math/Quaternion.js";
|
|
3093
|
+
import { Vector3 as Vector38 } from "three/src/math/Vector3.js";
|
|
3094
|
+
import { Group as Group3 } from "three/src/objects/Group.js";
|
|
3095
|
+
import { Mesh as Mesh3 } from "three/src/objects/Mesh.js";
|
|
3096
|
+
var DEGREE = TAU / 360;
|
|
3097
|
+
var SLOT_ANGLE_DEGREES = 30;
|
|
3098
|
+
var WEDGES_PER_LAYER = 12;
|
|
3099
|
+
var CUBE_HALF_EDGE = 1;
|
|
3100
|
+
var EQUATOR_HALF_HEIGHT = 0.25;
|
|
3101
|
+
var PUZZLE_SCALE = 0.5;
|
|
3102
|
+
var DEFAULT_FACELET_SCALE = 0.85;
|
|
3103
|
+
var STICKER_ELEVATION = 4e-3;
|
|
3104
|
+
var COLOR_UP = 16776960;
|
|
3105
|
+
var COLOR_DOWN = 16777215;
|
|
3106
|
+
var RING_COLORS = [2254591, 16750848, 65280, 16711680];
|
|
3107
|
+
var FRAME_OFFSET_DEGREES = 180;
|
|
3108
|
+
var BODY_COLOR = 1118481;
|
|
3109
|
+
var MIRROR = -1;
|
|
3110
|
+
var Y_AXIS = new Vector38(0, 1, 0);
|
|
3111
|
+
var SLICE_AXIS = new Vector38(
|
|
3112
|
+
MIRROR * Math.sin(15 * DEGREE),
|
|
3113
|
+
0,
|
|
3114
|
+
Math.cos(15 * DEGREE)
|
|
3115
|
+
).normalize();
|
|
3116
|
+
var SLICE_QUATERNION = new Quaternion2().setFromAxisAngle(SLICE_AXIS, TAU / 2);
|
|
3117
|
+
var HALF_TURN_Y = new Quaternion2().setFromAxisAngle(Y_AXIS, TAU / 2);
|
|
3118
|
+
function slotAzimuthDegrees(slot) {
|
|
3119
|
+
return FRAME_OFFSET_DEGREES + (slot < WEDGES_PER_LAYER ? SLOT_ANGLE_DEGREES * slot + 120 : 90 - SLOT_ANGLE_DEGREES * (slot - WEDGES_PER_LAYER));
|
|
3120
|
+
}
|
|
3121
|
+
function slotQuaternion(slot) {
|
|
3122
|
+
const quaternion = new Quaternion2().setFromAxisAngle(
|
|
3123
|
+
Y_AXIS,
|
|
3124
|
+
MIRROR * slotAzimuthDegrees(slot) * DEGREE
|
|
3125
|
+
);
|
|
3126
|
+
if (slot >= WEDGES_PER_LAYER) {
|
|
3127
|
+
quaternion.multiply(
|
|
3128
|
+
new Quaternion2().setFromAxisAngle(new Vector38(0, 0, 1), TAU / 2)
|
|
3129
|
+
);
|
|
3130
|
+
}
|
|
3131
|
+
return quaternion;
|
|
3132
|
+
}
|
|
3133
|
+
var SLOT_QUATERNIONS = new Array(2 * WEDGES_PER_LAYER).fill(null).map((_, slot) => slotQuaternion(slot));
|
|
3134
|
+
function wedgeShape(homeSlot) {
|
|
3135
|
+
const phase = homeSlot < WEDGES_PER_LAYER ? homeSlot % 3 : (homeSlot - WEDGES_PER_LAYER + 2) % 3;
|
|
3136
|
+
return phase === 0 ? "cornerHigh" : phase === 1 ? "cornerLow" : "edge";
|
|
3137
|
+
}
|
|
3138
|
+
function outerFaceOffsetDegrees(shape) {
|
|
3139
|
+
switch (shape) {
|
|
3140
|
+
case "cornerHigh":
|
|
3141
|
+
return -30;
|
|
3142
|
+
case "cornerLow":
|
|
3143
|
+
return 30;
|
|
3144
|
+
case "edge":
|
|
3145
|
+
return 0;
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
function squarePoint(phiDegrees) {
|
|
3149
|
+
return polarPoint(phiDegrees, Math.round(phiDegrees / 90) * 90);
|
|
3150
|
+
}
|
|
3151
|
+
function polarPoint(phiDegrees, faceCenterDegrees) {
|
|
1939
3152
|
const radius = CUBE_HALF_EDGE / Math.cos((phiDegrees - faceCenterDegrees) * DEGREE);
|
|
1940
3153
|
const phi = phiDegrees * DEGREE;
|
|
1941
3154
|
return [MIRROR * radius * Math.sin(phi), radius * Math.cos(phi)];
|
|
@@ -2001,15 +3214,15 @@ function prismStickerSpecs(spec) {
|
|
|
2001
3214
|
const specs = [];
|
|
2002
3215
|
if (topColor !== null) {
|
|
2003
3216
|
specs.push({
|
|
2004
|
-
corners: polygon.map(([x, z]) => new
|
|
2005
|
-
normal: new
|
|
3217
|
+
corners: polygon.map(([x, z]) => new Vector38(x, yTop, z)),
|
|
3218
|
+
normal: new Vector38(0, 1, 0),
|
|
2006
3219
|
color: topColor
|
|
2007
3220
|
});
|
|
2008
3221
|
}
|
|
2009
3222
|
if (bottomColor !== null) {
|
|
2010
3223
|
specs.push({
|
|
2011
|
-
corners: polygon.map(([x, z]) => new
|
|
2012
|
-
normal: new
|
|
3224
|
+
corners: polygon.map(([x, z]) => new Vector38(x, yBottom, z)),
|
|
3225
|
+
normal: new Vector38(0, -1, 0),
|
|
2013
3226
|
color: bottomColor
|
|
2014
3227
|
});
|
|
2015
3228
|
}
|
|
@@ -2026,17 +3239,17 @@ function prismStickerSpecs(spec) {
|
|
|
2026
3239
|
}
|
|
2027
3240
|
const [x0, z0] = polygon[i];
|
|
2028
3241
|
const [x1, z1] = polygon[(i + 1) % polygon.length];
|
|
2029
|
-
const normal2 = new
|
|
3242
|
+
const normal2 = new Vector38(z1 - z0, 0, -(x1 - x0)).normalize();
|
|
2030
3243
|
const outward = normal2.x * ((x0 + x1) / 2 - centroidX) + normal2.z * ((z0 + z1) / 2 - centroidZ);
|
|
2031
3244
|
if (outward < 0) {
|
|
2032
3245
|
normal2.negate();
|
|
2033
3246
|
}
|
|
2034
3247
|
specs.push({
|
|
2035
3248
|
corners: [
|
|
2036
|
-
new
|
|
2037
|
-
new
|
|
2038
|
-
new
|
|
2039
|
-
new
|
|
3249
|
+
new Vector38(x0, yBottom, z0),
|
|
3250
|
+
new Vector38(x1, yBottom, z1),
|
|
3251
|
+
new Vector38(x1, yTop, z1),
|
|
3252
|
+
new Vector38(x0, yTop, z0)
|
|
2040
3253
|
],
|
|
2041
3254
|
normal: normal2,
|
|
2042
3255
|
color
|
|
@@ -2045,7 +3258,7 @@ function prismStickerSpecs(spec) {
|
|
|
2045
3258
|
return specs;
|
|
2046
3259
|
}
|
|
2047
3260
|
function stickerPositions(spec, faceletScale) {
|
|
2048
|
-
const center = new
|
|
3261
|
+
const center = new Vector38();
|
|
2049
3262
|
for (const corner of spec.corners) {
|
|
2050
3263
|
center.addScaledVector(corner, 1 / spec.corners.length);
|
|
2051
3264
|
}
|
|
@@ -2068,16 +3281,87 @@ function stickerPositions(spec, faceletScale) {
|
|
|
2068
3281
|
}
|
|
2069
3282
|
return new Float32Array(out);
|
|
2070
3283
|
}
|
|
2071
|
-
var
|
|
2072
|
-
|
|
3284
|
+
var PIECE_GROOVE_HALF_WIDTH = 0.018;
|
|
3285
|
+
var PIECE_CHAMFER = 5e-3;
|
|
3286
|
+
var PUZZLE_RADIUS = CUBE_HALF_EDGE * Math.SQRT2;
|
|
3287
|
+
function prismPlanes(spec) {
|
|
3288
|
+
const { polygon, sideColors } = spec;
|
|
3289
|
+
const planes = [
|
|
3290
|
+
{ normal: new Vector38(0, 1, 0), offset: spec.yTop, color: spec.topColor },
|
|
3291
|
+
{
|
|
3292
|
+
normal: new Vector38(0, -1, 0),
|
|
3293
|
+
offset: -spec.yBottom,
|
|
3294
|
+
color: spec.bottomColor
|
|
3295
|
+
}
|
|
3296
|
+
];
|
|
3297
|
+
let centroidX = 0;
|
|
3298
|
+
let centroidZ = 0;
|
|
3299
|
+
for (const [x, z] of polygon) {
|
|
3300
|
+
centroidX += x / polygon.length;
|
|
3301
|
+
centroidZ += z / polygon.length;
|
|
3302
|
+
}
|
|
3303
|
+
for (let i = 0; i < polygon.length; i++) {
|
|
3304
|
+
const [x0, z0] = polygon[i];
|
|
3305
|
+
const [x1, z1] = polygon[(i + 1) % polygon.length];
|
|
3306
|
+
const normal2 = new Vector38(z1 - z0, 0, -(x1 - x0)).normalize();
|
|
3307
|
+
if (normal2.x * (x0 - centroidX) + normal2.z * (z0 - centroidZ) < 0) {
|
|
3308
|
+
normal2.negate();
|
|
3309
|
+
}
|
|
3310
|
+
planes.push({
|
|
3311
|
+
normal: normal2,
|
|
3312
|
+
offset: normal2.x * x0 + normal2.z * z0,
|
|
3313
|
+
color: sideColors[i] ?? null
|
|
3314
|
+
});
|
|
3315
|
+
}
|
|
3316
|
+
return planes;
|
|
3317
|
+
}
|
|
3318
|
+
function stickerlessPieceGeometry(spec) {
|
|
3319
|
+
const planes = prismPlanes(spec);
|
|
3320
|
+
const piece = solidPieceGeometry(planes, {
|
|
3321
|
+
groove: PIECE_GROOVE_HALF_WIDTH,
|
|
3322
|
+
chamfer: PIECE_CHAMFER,
|
|
3323
|
+
radius: PUZZLE_RADIUS
|
|
3324
|
+
});
|
|
3325
|
+
if (!piece) {
|
|
3326
|
+
throw new Error("Could not build a Square-1 piece out of its planes.");
|
|
3327
|
+
}
|
|
3328
|
+
const colors = new Uint8Array(4 * piece.vertexCount);
|
|
3329
|
+
const paint = (ranges, color) => {
|
|
3330
|
+
for (const range of ranges) {
|
|
3331
|
+
for (let i = range.start; i < range.start + range.count; i++) {
|
|
3332
|
+
colors[4 * i] = color >> 16 & 255;
|
|
3333
|
+
colors[4 * i + 1] = color >> 8 & 255;
|
|
3334
|
+
colors[4 * i + 2] = color & 255;
|
|
3335
|
+
colors[4 * i + 3] = 255;
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
};
|
|
3339
|
+
paint(piece.internalRanges, bodyMaskColors.internal);
|
|
3340
|
+
for (let i = 0; i < planes.length; i++) {
|
|
3341
|
+
const color = planes[i].color;
|
|
3342
|
+
if (color !== null) {
|
|
3343
|
+
paint(piece.ranges[i], color);
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
const geometry = new BufferGeometry6();
|
|
3347
|
+
geometry.setAttribute("position", new BufferAttribute6(piece.positions, 3));
|
|
3348
|
+
geometry.setAttribute("color", new BufferAttribute6(colors, 4, true));
|
|
3349
|
+
return geometry;
|
|
3350
|
+
}
|
|
3351
|
+
var bodyMaterial = new MeshBasicMaterial4({
|
|
3352
|
+
color: new Color5(BODY_COLOR).convertLinearToSRGB(),
|
|
2073
3353
|
side: DoubleSide3
|
|
2074
3354
|
});
|
|
3355
|
+
var stickerlessMaterialCache;
|
|
3356
|
+
function stickerlessMaterial() {
|
|
3357
|
+
return stickerlessMaterialCache ??= newVertexColorBodyMaterial();
|
|
3358
|
+
}
|
|
2075
3359
|
var stickerMaterialCache = /* @__PURE__ */ new Map();
|
|
2076
3360
|
function stickerMaterial(color) {
|
|
2077
3361
|
let material = stickerMaterialCache.get(color);
|
|
2078
3362
|
if (!material) {
|
|
2079
|
-
material = new
|
|
2080
|
-
color: new
|
|
3363
|
+
material = new MeshBasicMaterial4({
|
|
3364
|
+
color: new Color5(color).convertLinearToSRGB(),
|
|
2081
3365
|
side: DoubleSide3
|
|
2082
3366
|
});
|
|
2083
3367
|
stickerMaterialCache.set(color, material);
|
|
@@ -2086,23 +3370,31 @@ function stickerMaterial(color) {
|
|
|
2086
3370
|
}
|
|
2087
3371
|
var Square1Piece = class extends Group3 {
|
|
2088
3372
|
body;
|
|
2089
|
-
#stickerSpecs;
|
|
3373
|
+
#stickerSpecs = [];
|
|
2090
3374
|
#stickerMeshes = [];
|
|
2091
|
-
constructor(spec, faceletScale) {
|
|
3375
|
+
constructor(spec, faceletScale, stickerless) {
|
|
2092
3376
|
super();
|
|
2093
|
-
|
|
3377
|
+
if (stickerless) {
|
|
3378
|
+
this.body = new Mesh3(
|
|
3379
|
+
stickerlessPieceGeometry(spec),
|
|
3380
|
+
stickerlessMaterial()
|
|
3381
|
+
);
|
|
3382
|
+
this.add(this.body);
|
|
3383
|
+
return;
|
|
3384
|
+
}
|
|
3385
|
+
const bodyGeometry = new BufferGeometry6();
|
|
2094
3386
|
bodyGeometry.setAttribute(
|
|
2095
3387
|
"position",
|
|
2096
|
-
new
|
|
3388
|
+
new BufferAttribute6(prismBodyPositions(spec), 3)
|
|
2097
3389
|
);
|
|
2098
3390
|
this.body = new Mesh3(bodyGeometry, bodyMaterial);
|
|
2099
3391
|
this.add(this.body);
|
|
2100
3392
|
this.#stickerSpecs = prismStickerSpecs(spec);
|
|
2101
3393
|
for (const stickerSpec of this.#stickerSpecs) {
|
|
2102
|
-
const geometry = new
|
|
3394
|
+
const geometry = new BufferGeometry6();
|
|
2103
3395
|
geometry.setAttribute(
|
|
2104
3396
|
"position",
|
|
2105
|
-
new
|
|
3397
|
+
new BufferAttribute6(stickerPositions(stickerSpec, faceletScale), 3)
|
|
2106
3398
|
);
|
|
2107
3399
|
const mesh = new Mesh3(geometry, stickerMaterial(stickerSpec.color));
|
|
2108
3400
|
this.#stickerMeshes.push(mesh);
|
|
@@ -2114,7 +3406,7 @@ var Square1Piece = class extends Group3 {
|
|
|
2114
3406
|
const geometry = this.#stickerMeshes[i].geometry;
|
|
2115
3407
|
geometry.setAttribute(
|
|
2116
3408
|
"position",
|
|
2117
|
-
new
|
|
3409
|
+
new BufferAttribute6(
|
|
2118
3410
|
stickerPositions(this.#stickerSpecs[i], faceletScale),
|
|
2119
3411
|
3
|
|
2120
3412
|
)
|
|
@@ -2122,7 +3414,9 @@ var Square1Piece = class extends Group3 {
|
|
|
2122
3414
|
}
|
|
2123
3415
|
}
|
|
2124
3416
|
setFoundationVisible(visible) {
|
|
2125
|
-
this.
|
|
3417
|
+
if (this.#stickerMeshes.length > 0) {
|
|
3418
|
+
this.body.visible = visible;
|
|
3419
|
+
}
|
|
2126
3420
|
}
|
|
2127
3421
|
};
|
|
2128
3422
|
function wedgePrismSpec(homeSlot) {
|
|
@@ -2186,8 +3480,15 @@ var Square1_3D = class extends Object3D3 {
|
|
|
2186
3480
|
if (typeof options.showFoundation === "boolean") {
|
|
2187
3481
|
this.#showFoundation = options.showFoundation;
|
|
2188
3482
|
}
|
|
3483
|
+
if (options.experimentalWedgeStyle) {
|
|
3484
|
+
this.#stickerless = options.experimentalWedgeStyle === "stickerless";
|
|
3485
|
+
}
|
|
2189
3486
|
for (let piece = 0; piece < 2 * WEDGES_PER_LAYER; piece++) {
|
|
2190
|
-
const wedge = new Square1Piece(
|
|
3487
|
+
const wedge = new Square1Piece(
|
|
3488
|
+
wedgePrismSpec(piece),
|
|
3489
|
+
this.#faceletScale,
|
|
3490
|
+
this.#stickerless
|
|
3491
|
+
);
|
|
2191
3492
|
wedge.setFoundationVisible(this.#showFoundation);
|
|
2192
3493
|
wedge.quaternion.copy(SLOT_QUATERNIONS[piece]);
|
|
2193
3494
|
this.#wedgePieces.push(wedge);
|
|
@@ -2196,7 +3497,8 @@ var Square1_3D = class extends Object3D3 {
|
|
|
2196
3497
|
for (const pair of CORNER_PAIRS) {
|
|
2197
3498
|
const corner = new Square1Piece(
|
|
2198
3499
|
cornerPrismSpec(pair),
|
|
2199
|
-
this.#faceletScale
|
|
3500
|
+
this.#faceletScale,
|
|
3501
|
+
this.#stickerless
|
|
2200
3502
|
);
|
|
2201
3503
|
corner.setFoundationVisible(this.#showFoundation);
|
|
2202
3504
|
corner.visible = false;
|
|
@@ -2206,7 +3508,8 @@ var Square1_3D = class extends Object3D3 {
|
|
|
2206
3508
|
for (let piece = 0; piece < 2; piece++) {
|
|
2207
3509
|
const equator = new Square1Piece(
|
|
2208
3510
|
equatorPrismSpec(piece),
|
|
2209
|
-
this.#faceletScale
|
|
3511
|
+
this.#faceletScale,
|
|
3512
|
+
this.#stickerless
|
|
2210
3513
|
);
|
|
2211
3514
|
equator.setFoundationVisible(this.#showFoundation);
|
|
2212
3515
|
this.#equatorPieces.push(equator);
|
|
@@ -2220,6 +3523,7 @@ var Square1_3D = class extends Object3D3 {
|
|
|
2220
3523
|
#equatorPieces = [];
|
|
2221
3524
|
#faceletScale = DEFAULT_FACELET_SCALE;
|
|
2222
3525
|
#showFoundation = true;
|
|
3526
|
+
#stickerless = true;
|
|
2223
3527
|
#slotOfPiece = new Int8Array(2 * WEDGES_PER_LAYER);
|
|
2224
3528
|
#slotMoveMask = new Uint8Array(2 * WEDGES_PER_LAYER);
|
|
2225
3529
|
#slotRotation = new Array(2 * WEDGES_PER_LAYER).fill(
|
|
@@ -2345,13 +3649,444 @@ var Square1_3D = class extends Object3D3 {
|
|
|
2345
3649
|
}
|
|
2346
3650
|
};
|
|
2347
3651
|
|
|
3652
|
+
// src/cubing/twisty/views/3D/puzzles/Stickerless3D.ts
|
|
3653
|
+
import { BackSide as BackSide2 } from "three/src/constants.js";
|
|
3654
|
+
import { BufferAttribute as BufferAttribute7 } from "three/src/core/BufferAttribute.js";
|
|
3655
|
+
import { BufferGeometry as BufferGeometry7 } from "three/src/core/BufferGeometry.js";
|
|
3656
|
+
import { Object3D as Object3D4 } from "three/src/core/Object3D.js";
|
|
3657
|
+
import { MeshBasicMaterial as MeshBasicMaterial5 } from "three/src/materials/MeshBasicMaterial.js";
|
|
3658
|
+
import { Matrix4 as Matrix44 } from "three/src/math/Matrix4.js";
|
|
3659
|
+
import { Vector3 as Vector39 } from "three/src/math/Vector3.js";
|
|
3660
|
+
import { BatchedMesh } from "three/src/objects/BatchedMesh.js";
|
|
3661
|
+
import { Mesh as Mesh4 } from "three/src/objects/Mesh.js";
|
|
3662
|
+
var invisibleMaterial2 = new MeshBasicMaterial5({ visible: false });
|
|
3663
|
+
function newBatch(meshes, material) {
|
|
3664
|
+
let vertices = 0;
|
|
3665
|
+
let indices = 0;
|
|
3666
|
+
for (const { geometry } of meshes) {
|
|
3667
|
+
vertices += geometry.getAttribute("position").count;
|
|
3668
|
+
indices += geometry.getIndex()?.count ?? 0;
|
|
3669
|
+
}
|
|
3670
|
+
return new BatchedMesh(meshes.length, vertices, indices, material);
|
|
3671
|
+
}
|
|
3672
|
+
function addToBatch(batch, mesh, home) {
|
|
3673
|
+
const geometryId = batch.addGeometry(mesh.geometry);
|
|
3674
|
+
const instance = batch.addInstance(geometryId);
|
|
3675
|
+
batch.setMatrixAt(instance, home);
|
|
3676
|
+
mesh.geometry.dispose();
|
|
3677
|
+
const range = batch.getGeometryRangeAt(geometryId);
|
|
3678
|
+
return { instance, vertexStart: range.vertexStart };
|
|
3679
|
+
}
|
|
3680
|
+
var Stickerless3D = class extends Object3D4 {
|
|
3681
|
+
constructor(scheduleRenderCallback, kpuzzle, stickerDat, plan, options = {}) {
|
|
3682
|
+
super();
|
|
3683
|
+
this.scheduleRenderCallback = scheduleRenderCallback;
|
|
3684
|
+
this.kpuzzle = kpuzzle;
|
|
3685
|
+
this.stickerDat = stickerDat;
|
|
3686
|
+
this.plan = plan;
|
|
3687
|
+
this.#hintMaterial = new MeshBasicMaterial5({
|
|
3688
|
+
vertexColors: true,
|
|
3689
|
+
transparent: true,
|
|
3690
|
+
// Hint facelets show the faces turned away from the camera, so they are
|
|
3691
|
+
// drawn from the inside out.
|
|
3692
|
+
side: BackSide2
|
|
3693
|
+
});
|
|
3694
|
+
this.#bodyMaterial = newVertexColorBodyMaterial();
|
|
3695
|
+
for (const axis of stickerDat.axis) {
|
|
3696
|
+
this.#axesInfo[axis.quantumMove.family] = {
|
|
3697
|
+
axis: new Vector39(...axis.coordinates),
|
|
3698
|
+
order: axis.order
|
|
3699
|
+
};
|
|
3700
|
+
}
|
|
3701
|
+
this.#bodyBatch = newBatch(
|
|
3702
|
+
plan.pieces.map((piecePlan) => piecePlan.body),
|
|
3703
|
+
this.#bodyMaterial
|
|
3704
|
+
);
|
|
3705
|
+
this.#bodyBatch.sortObjects = false;
|
|
3706
|
+
this.#bodyBatch.perObjectFrustumCulled = false;
|
|
3707
|
+
this.add(this.#bodyBatch);
|
|
3708
|
+
const hints = plan.pieces.map((piecePlan) => piecePlan.hint).filter((hint) => hint !== null);
|
|
3709
|
+
if (hints.length > 0) {
|
|
3710
|
+
this.#hintBatch = newBatch(hints, this.#hintMaterial);
|
|
3711
|
+
this.#hintBatch.perObjectFrustumCulled = false;
|
|
3712
|
+
this.add(this.#hintBatch);
|
|
3713
|
+
}
|
|
3714
|
+
for (const piecePlan of plan.pieces) {
|
|
3715
|
+
this.#addPiece(piecePlan);
|
|
3716
|
+
}
|
|
3717
|
+
this.#bodyColors = this.#bodyBatch.geometry.getAttribute(
|
|
3718
|
+
"color"
|
|
3719
|
+
);
|
|
3720
|
+
this.#hintColors = this.#hintBatch?.geometry.getAttribute("color") ?? null;
|
|
3721
|
+
this.experimentalUpdateOptions({
|
|
3722
|
+
hintFacelets: options.hintFacelets ?? "floating"
|
|
3723
|
+
});
|
|
3724
|
+
for (const face2 of stickerDat.faces) {
|
|
3725
|
+
this.#addControlTarget(face2);
|
|
3726
|
+
}
|
|
3727
|
+
this.scale.setScalar(plan.scale);
|
|
3728
|
+
if (options.stickeringMask) {
|
|
3729
|
+
this.setStickeringMask(options.stickeringMask);
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
scheduleRenderCallback;
|
|
3733
|
+
kpuzzle;
|
|
3734
|
+
stickerDat;
|
|
3735
|
+
plan;
|
|
3736
|
+
#pieces = [];
|
|
3737
|
+
/**
|
|
3738
|
+
* One draw call for every piece of the puzzle, and one for every hint
|
|
3739
|
+
* facelet. A batch keeps a matrix per instance, so a move still moves the
|
|
3740
|
+
* pieces it sweeps and nothing else.
|
|
3741
|
+
*/
|
|
3742
|
+
#bodyBatch;
|
|
3743
|
+
#hintBatch = null;
|
|
3744
|
+
#bodyColors;
|
|
3745
|
+
#hintColors = null;
|
|
3746
|
+
/** Indexed `[orbit][ori][ord]`, like the orbits of the `KPuzzle`. */
|
|
3747
|
+
#facelets = {};
|
|
3748
|
+
#axesInfo = {};
|
|
3749
|
+
#controlTargets = [];
|
|
3750
|
+
#bodyMaterial;
|
|
3751
|
+
#hintMaterial;
|
|
3752
|
+
#appearances = /* @__PURE__ */ new Map();
|
|
3753
|
+
/** Pieces rotated away from their home position by the last frame. */
|
|
3754
|
+
#turningPieces = [];
|
|
3755
|
+
/** Which pieces each quantum move sweeps, by the move's notation. */
|
|
3756
|
+
#turnedPieces = /* @__PURE__ */ new Map();
|
|
3757
|
+
/** The span of each color attribute that changed, so only it is uploaded. */
|
|
3758
|
+
#dirtyColors = /* @__PURE__ */ new Map();
|
|
3759
|
+
#lastPosition = null;
|
|
3760
|
+
#pendingStickeringUpdate = false;
|
|
3761
|
+
#addPiece(plan) {
|
|
3762
|
+
const bodyInstance = addToBatch(this.#bodyBatch, plan.body, plan.home);
|
|
3763
|
+
const hintInstance = plan.hint && this.#hintBatch ? addToBatch(this.#hintBatch, plan.hint, plan.home) : -1;
|
|
3764
|
+
const piece = {
|
|
3765
|
+
bodyInstance: bodyInstance.instance,
|
|
3766
|
+
hintInstance: hintInstance === -1 ? -1 : hintInstance.instance,
|
|
3767
|
+
home: plan.home,
|
|
3768
|
+
matrix: plan.home.clone(),
|
|
3769
|
+
turning: false
|
|
3770
|
+
};
|
|
3771
|
+
this.#pieces.push(piece);
|
|
3772
|
+
const shift = (ranges, offset) => ranges.map((range) => ({
|
|
3773
|
+
start: range.start + offset,
|
|
3774
|
+
count: range.count
|
|
3775
|
+
}));
|
|
3776
|
+
const orbitFacelets = this.#facelets[plan.orbit] ??= [];
|
|
3777
|
+
for (const facelet of plan.facelets) {
|
|
3778
|
+
(orbitFacelets[facelet.ori] ??= [])[plan.ord] = {
|
|
3779
|
+
piece,
|
|
3780
|
+
faceStyle: facelet.faceStyle,
|
|
3781
|
+
body: shift(facelet.body, bodyInstance.vertexStart),
|
|
3782
|
+
hint: hintInstance === -1 ? [] : shift(facelet.hint, hintInstance.vertexStart),
|
|
3783
|
+
appearanceKey: -1,
|
|
3784
|
+
mask: "regular",
|
|
3785
|
+
hintMask: "regular"
|
|
3786
|
+
};
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
#setMatrix(piece) {
|
|
3790
|
+
this.#bodyBatch.setMatrixAt(piece.bodyInstance, piece.matrix);
|
|
3791
|
+
if (piece.hintInstance !== -1) {
|
|
3792
|
+
this.#hintBatch.setMatrixAt(piece.hintInstance, piece.matrix);
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
#addControlTarget(face2) {
|
|
3796
|
+
const { coords } = face2;
|
|
3797
|
+
const triangles = coords.length / 3 - 2;
|
|
3798
|
+
const positions = new Float32Array(9 * triangles);
|
|
3799
|
+
for (let triangle = 0; triangle < triangles; triangle++) {
|
|
3800
|
+
for (const [vertex, source] of [
|
|
3801
|
+
0,
|
|
3802
|
+
triangle + 1,
|
|
3803
|
+
triangle + 2
|
|
3804
|
+
].entries()) {
|
|
3805
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
3806
|
+
positions[9 * triangle + 3 * vertex + axis] = coords[3 * source + axis];
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
const geometry = new BufferGeometry7();
|
|
3811
|
+
geometry.setAttribute("position", new BufferAttribute7(positions, 3));
|
|
3812
|
+
const mesh = new Mesh4(geometry, invisibleMaterial2);
|
|
3813
|
+
mesh.userData["quantumMove"] = this.stickerDat.notationMapper.notationToExternal(new Move(face2.name));
|
|
3814
|
+
mesh.scale.setScalar(0.99);
|
|
3815
|
+
this.add(mesh);
|
|
3816
|
+
this.#controlTargets.push(mesh);
|
|
3817
|
+
}
|
|
3818
|
+
dispose() {
|
|
3819
|
+
this.#bodyBatch.dispose();
|
|
3820
|
+
this.#bodyBatch.geometry.dispose();
|
|
3821
|
+
this.#hintBatch?.dispose();
|
|
3822
|
+
this.#hintBatch?.geometry.dispose();
|
|
3823
|
+
this.#bodyMaterial.dispose();
|
|
3824
|
+
this.#hintMaterial.dispose();
|
|
3825
|
+
}
|
|
3826
|
+
experimentalGetStickerTargets() {
|
|
3827
|
+
return [];
|
|
3828
|
+
}
|
|
3829
|
+
experimentalGetControlTargets() {
|
|
3830
|
+
return this.#controlTargets;
|
|
3831
|
+
}
|
|
3832
|
+
#isValidMove(move) {
|
|
3833
|
+
try {
|
|
3834
|
+
this.kpuzzle.moveToTransformation(move);
|
|
3835
|
+
return true;
|
|
3836
|
+
} catch {
|
|
3837
|
+
return false;
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3840
|
+
getClosestMoveToAxis(point, transformations) {
|
|
3841
|
+
let closestMove = null;
|
|
3842
|
+
let closestMoveDotProduct = 0;
|
|
3843
|
+
let modify = (m) => m;
|
|
3844
|
+
switch (transformations.depth) {
|
|
3845
|
+
case "secondSlice": {
|
|
3846
|
+
modify = (m) => m.modified({ innerLayer: 2 });
|
|
3847
|
+
break;
|
|
3848
|
+
}
|
|
3849
|
+
case "rotation": {
|
|
3850
|
+
modify = (m) => m.modified({ family: `${m.family}v` });
|
|
3851
|
+
break;
|
|
3852
|
+
}
|
|
3853
|
+
}
|
|
3854
|
+
for (const axis of this.stickerDat.axis) {
|
|
3855
|
+
const product = point.dot(new Vector39(...axis.coordinates));
|
|
3856
|
+
if (product > closestMoveDotProduct) {
|
|
3857
|
+
const modified = this.stickerDat.notationMapper.notationToExternal(
|
|
3858
|
+
modify(axis.quantumMove)
|
|
3859
|
+
);
|
|
3860
|
+
if (!modified || !this.#isValidMove(modified)) {
|
|
3861
|
+
continue;
|
|
3862
|
+
}
|
|
3863
|
+
closestMoveDotProduct = product;
|
|
3864
|
+
closestMove = modified;
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
if (!closestMove) {
|
|
3868
|
+
return null;
|
|
3869
|
+
}
|
|
3870
|
+
if (transformations.invert) {
|
|
3871
|
+
closestMove = closestMove.invert();
|
|
3872
|
+
}
|
|
3873
|
+
const order = this.kpuzzle.moveToTransformation(closestMove).repetitionOrder();
|
|
3874
|
+
return { move: closestMove, order };
|
|
3875
|
+
}
|
|
3876
|
+
setStickeringMask(stickeringMask) {
|
|
3877
|
+
for (const orbitDefinition of stickeringMask.specialBehaviour === "picture" ? [] : this.kpuzzle.definition.orbits) {
|
|
3878
|
+
const { orbitName, numPieces, numOrientations } = orbitDefinition;
|
|
3879
|
+
const orbitFacelets = this.#facelets[orbitName];
|
|
3880
|
+
if (!orbitFacelets) {
|
|
3881
|
+
continue;
|
|
3882
|
+
}
|
|
3883
|
+
for (let ori = 0; ori < numOrientations; ori++) {
|
|
3884
|
+
for (let ord = 0; ord < numPieces; ord++) {
|
|
3885
|
+
const facelet = orbitFacelets[ori]?.[ord];
|
|
3886
|
+
if (!facelet) {
|
|
3887
|
+
continue;
|
|
3888
|
+
}
|
|
3889
|
+
facelet.mask = getFaceletStickeringMask(
|
|
3890
|
+
stickeringMask,
|
|
3891
|
+
orbitName,
|
|
3892
|
+
ord,
|
|
3893
|
+
ori,
|
|
3894
|
+
false
|
|
3895
|
+
);
|
|
3896
|
+
facelet.hintMask = getFaceletStickeringMask(
|
|
3897
|
+
stickeringMask,
|
|
3898
|
+
orbitName,
|
|
3899
|
+
ord,
|
|
3900
|
+
ori,
|
|
3901
|
+
true
|
|
3902
|
+
);
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
this.#pendingStickeringUpdate = true;
|
|
3907
|
+
if (this.#lastPosition) {
|
|
3908
|
+
this.onPositionChange(this.#lastPosition);
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3911
|
+
/** @deprecated */
|
|
3912
|
+
experimentalUpdateOptions(options) {
|
|
3913
|
+
if (options.hintFacelets !== void 0 && this.#hintBatch) {
|
|
3914
|
+
this.#hintBatch.visible = options.hintFacelets !== "none";
|
|
3915
|
+
this.scheduleRenderCallback();
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
/** @deprecated */
|
|
3919
|
+
experimentalUpdateTexture(enabled, stickerTexture, _hintTexture) {
|
|
3920
|
+
if (enabled && stickerTexture) {
|
|
3921
|
+
console.warn(
|
|
3922
|
+
"Sprites are not supported for stickerless pieces. For now, set the sprite before creating the TwistyPlayer."
|
|
3923
|
+
);
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
onPositionChange(position) {
|
|
3927
|
+
const { pattern } = position;
|
|
3928
|
+
if (this.#pendingStickeringUpdate || !this.#lastPosition || !this.#lastPosition.pattern.isIdentical(pattern)) {
|
|
3929
|
+
this.#updateColors(position);
|
|
3930
|
+
this.#lastPosition = position;
|
|
3931
|
+
this.#pendingStickeringUpdate = false;
|
|
3932
|
+
}
|
|
3933
|
+
const wereTurning = this.#turningPieces;
|
|
3934
|
+
for (const piece of wereTurning) {
|
|
3935
|
+
piece.turning = false;
|
|
3936
|
+
}
|
|
3937
|
+
this.#turningPieces = [];
|
|
3938
|
+
const turnMatrix = new Matrix44();
|
|
3939
|
+
for (const moveProgress of position.movesInProgress) {
|
|
3940
|
+
const move = moveProgress.move;
|
|
3941
|
+
const unswizzled = this.stickerDat.unswizzle(move);
|
|
3942
|
+
if (!unswizzled) {
|
|
3943
|
+
continue;
|
|
3944
|
+
}
|
|
3945
|
+
const axisInfo = this.#axesInfo[unswizzled.family];
|
|
3946
|
+
if (!axisInfo) {
|
|
3947
|
+
continue;
|
|
3948
|
+
}
|
|
3949
|
+
turnMatrix.makeRotationAxis(
|
|
3950
|
+
axisInfo.axis,
|
|
3951
|
+
-smootherStep(moveProgress.fraction) * moveProgress.direction * unswizzled.amount * TAU / axisInfo.order
|
|
3952
|
+
);
|
|
3953
|
+
for (const piece of this.#piecesTurnedBy(move)) {
|
|
3954
|
+
if (!piece.turning) {
|
|
3955
|
+
piece.turning = true;
|
|
3956
|
+
piece.matrix.copy(piece.home);
|
|
3957
|
+
this.#turningPieces.push(piece);
|
|
3958
|
+
}
|
|
3959
|
+
piece.matrix.premultiply(turnMatrix);
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
for (const piece of wereTurning) {
|
|
3963
|
+
if (!piece.turning) {
|
|
3964
|
+
piece.matrix.copy(piece.home);
|
|
3965
|
+
this.#setMatrix(piece);
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
for (const piece of this.#turningPieces) {
|
|
3969
|
+
this.#setMatrix(piece);
|
|
3970
|
+
}
|
|
3971
|
+
this.scheduleRenderCallback();
|
|
3972
|
+
}
|
|
3973
|
+
/**
|
|
3974
|
+
* The pieces a single quantum of `move` sweeps: the ones whose slot does not
|
|
3975
|
+
* map to itself.
|
|
3976
|
+
*/
|
|
3977
|
+
#piecesTurnedBy(move) {
|
|
3978
|
+
const quantum = move.modified({ amount: 1 });
|
|
3979
|
+
const key = quantum.toString();
|
|
3980
|
+
const cached = this.#turnedPieces.get(key);
|
|
3981
|
+
if (cached) {
|
|
3982
|
+
return cached;
|
|
3983
|
+
}
|
|
3984
|
+
const pieces = [];
|
|
3985
|
+
const { transformationData } = this.#quantumTransformation(quantum);
|
|
3986
|
+
for (const orbitName in this.#facelets) {
|
|
3987
|
+
const orbitTransformation = transformationData[orbitName];
|
|
3988
|
+
if (!orbitTransformation) {
|
|
3989
|
+
continue;
|
|
3990
|
+
}
|
|
3991
|
+
const { permutation, orientationDelta } = orbitTransformation;
|
|
3992
|
+
const slots = this.#facelets[orbitName][0];
|
|
3993
|
+
for (let ord = 0; ord < permutation.length; ord++) {
|
|
3994
|
+
if (permutation[ord] !== ord || orientationDelta[ord] !== 0) {
|
|
3995
|
+
const slot = slots[ord];
|
|
3996
|
+
if (slot) {
|
|
3997
|
+
pieces.push(slot.piece);
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
4002
|
+
this.#turnedPieces.set(key, pieces);
|
|
4003
|
+
return pieces;
|
|
4004
|
+
}
|
|
4005
|
+
#quantumTransformation(quantum) {
|
|
4006
|
+
try {
|
|
4007
|
+
return this.kpuzzle.moveToTransformation(quantum);
|
|
4008
|
+
} catch (e) {
|
|
4009
|
+
const internal = this.stickerDat.notationMapper.notationToInternal(quantum);
|
|
4010
|
+
const external = internal && this.stickerDat.notationMapper.notationToExternal(
|
|
4011
|
+
internal.modified({ amount: 1 })
|
|
4012
|
+
);
|
|
4013
|
+
if (!external) {
|
|
4014
|
+
throw e;
|
|
4015
|
+
}
|
|
4016
|
+
return this.kpuzzle.moveToTransformation(external);
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
4019
|
+
/** Repaints every slot with the colors of whatever piece is now in it. */
|
|
4020
|
+
#updateColors(position) {
|
|
4021
|
+
for (const orbitName in this.#facelets) {
|
|
4022
|
+
const orbitFacelets = this.#facelets[orbitName];
|
|
4023
|
+
const orbitPattern = position.pattern.patternData[orbitName];
|
|
4024
|
+
const numOrientations = orbitFacelets.length;
|
|
4025
|
+
for (let ori = 0; ori < numOrientations; ori++) {
|
|
4026
|
+
const slots = orbitFacelets[ori];
|
|
4027
|
+
for (let ord = 0; ord < slots.length; ord++) {
|
|
4028
|
+
const slot = slots[ord];
|
|
4029
|
+
if (!slot) {
|
|
4030
|
+
continue;
|
|
4031
|
+
}
|
|
4032
|
+
const sourceOri = numOrientations === 1 ? 0 : (ori + numOrientations - orbitPattern.orientation[ord]) % numOrientations;
|
|
4033
|
+
const source = orbitFacelets[sourceOri][orbitPattern.pieces[ord]];
|
|
4034
|
+
const key = faceletAppearanceKey(
|
|
4035
|
+
source.faceStyle,
|
|
4036
|
+
source.mask,
|
|
4037
|
+
source.hintMask
|
|
4038
|
+
);
|
|
4039
|
+
if (key === slot.appearanceKey) {
|
|
4040
|
+
continue;
|
|
4041
|
+
}
|
|
4042
|
+
slot.appearanceKey = key;
|
|
4043
|
+
if (slot.body.length === 0 && slot.hint.length === 0) {
|
|
4044
|
+
continue;
|
|
4045
|
+
}
|
|
4046
|
+
const appearance = faceletAppearance(
|
|
4047
|
+
this.#appearances,
|
|
4048
|
+
this.plan.faceStyles,
|
|
4049
|
+
source.faceStyle,
|
|
4050
|
+
source.mask,
|
|
4051
|
+
source.hintMask
|
|
4052
|
+
);
|
|
4053
|
+
this.#paint(this.#bodyColors, slot.body, appearance, 0);
|
|
4054
|
+
if (this.#hintColors) {
|
|
4055
|
+
this.#paint(this.#hintColors, slot.hint, appearance, 4);
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
for (const [colors, span] of this.#dirtyColors) {
|
|
4061
|
+
colors.addUpdateRange(4 * span.first, 4 * (span.last - span.first + 1));
|
|
4062
|
+
colors.needsUpdate = true;
|
|
4063
|
+
}
|
|
4064
|
+
this.#dirtyColors.clear();
|
|
4065
|
+
}
|
|
4066
|
+
#paint(colors, ranges, appearance, offset) {
|
|
4067
|
+
if (ranges.length === 0) {
|
|
4068
|
+
return;
|
|
4069
|
+
}
|
|
4070
|
+
writeColor(colors, ranges, appearance, offset);
|
|
4071
|
+
let span = this.#dirtyColors.get(colors);
|
|
4072
|
+
if (!span) {
|
|
4073
|
+
span = { first: Number.POSITIVE_INFINITY, last: 0 };
|
|
4074
|
+
this.#dirtyColors.set(colors, span);
|
|
4075
|
+
}
|
|
4076
|
+
for (const range of ranges) {
|
|
4077
|
+
span.first = Math.min(span.first, range.start);
|
|
4078
|
+
span.last = Math.max(span.last, range.start + range.count - 1);
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
};
|
|
4082
|
+
|
|
2348
4083
|
// src/cubing/twisty/heavy-code-imports/dynamic-entries/twisty-dynamic-3d.ts
|
|
2349
4084
|
import { PerspectiveCamera } from "three/src/cameras/PerspectiveCamera.js";
|
|
2350
4085
|
import { Raycaster } from "three/src/core/Raycaster.js";
|
|
2351
4086
|
import { TextureLoader as TextureLoader2 } from "three/src/loaders/TextureLoader.js";
|
|
2352
4087
|
import { Spherical } from "three/src/math/Spherical.js";
|
|
2353
4088
|
import { Vector2 as Vector22 } from "three/src/math/Vector2.js";
|
|
2354
|
-
import { Vector3 as
|
|
4089
|
+
import { Vector3 as Vector310 } from "three/src/math/Vector3.js";
|
|
2355
4090
|
import { WebGLRenderer } from "three/src/renderers/WebGLRenderer.js";
|
|
2356
4091
|
import { Scene } from "three/src/scenes/Scene.js";
|
|
2357
4092
|
|
|
@@ -2393,11 +4128,19 @@ async function square1_3DShim(renderCallback, puzzleLoader, faceletScale) {
|
|
|
2393
4128
|
faceletScale
|
|
2394
4129
|
});
|
|
2395
4130
|
}
|
|
2396
|
-
async function pg3dShim(renderCallback, puzzleLoader, hintFacelets, faceletScale, darkIgnoredOrbits) {
|
|
4131
|
+
async function pg3dShim(renderCallback, puzzleLoader, hintFacelets, faceletScale, darkIgnoredOrbits, pictureCube = false) {
|
|
4132
|
+
const kpuzzle = await puzzleLoader.kpuzzle();
|
|
4133
|
+
const stickerDat = (await puzzleLoader.pg()).get3d({ darkIgnoredOrbits });
|
|
4134
|
+
const plan = darkIgnoredOrbits || pictureCube ? null : cubePuzzlePlan(stickerDat) ?? solidPuzzlePlan(stickerDat);
|
|
4135
|
+
if (plan) {
|
|
4136
|
+
return new Stickerless3D(renderCallback, kpuzzle, stickerDat, plan, {
|
|
4137
|
+
hintFacelets
|
|
4138
|
+
});
|
|
4139
|
+
}
|
|
2397
4140
|
return new PG3D(
|
|
2398
4141
|
renderCallback,
|
|
2399
|
-
|
|
2400
|
-
|
|
4142
|
+
kpuzzle,
|
|
4143
|
+
stickerDat,
|
|
2401
4144
|
true,
|
|
2402
4145
|
hintFacelets === "floating",
|
|
2403
4146
|
void 0,
|
|
@@ -2408,17 +4151,18 @@ export {
|
|
|
2408
4151
|
Cube3D,
|
|
2409
4152
|
PG3D,
|
|
2410
4153
|
Square1_3D,
|
|
4154
|
+
Stickerless3D,
|
|
2411
4155
|
PerspectiveCamera as ThreePerspectiveCamera,
|
|
2412
4156
|
Raycaster as ThreeRaycaster,
|
|
2413
4157
|
Scene as ThreeScene,
|
|
2414
4158
|
Spherical as ThreeSpherical,
|
|
2415
4159
|
TextureLoader2 as ThreeTextureLoader,
|
|
2416
4160
|
Vector22 as ThreeVector2,
|
|
2417
|
-
|
|
4161
|
+
Vector310 as ThreeVector3,
|
|
2418
4162
|
WebGLRenderer as ThreeWebGLRenderer,
|
|
2419
4163
|
Twisty3DScene,
|
|
2420
4164
|
cube3DShim,
|
|
2421
4165
|
pg3dShim,
|
|
2422
4166
|
square1_3DShim
|
|
2423
4167
|
};
|
|
2424
|
-
//# sourceMappingURL=twisty-dynamic-3d-
|
|
4168
|
+
//# sourceMappingURL=twisty-dynamic-3d-MF76FSDA.js.map
|