@rednaxela101/cubing 0.63.7 → 0.63.8
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 +16 -10
- package/dist/bin/chunks/{chunk-6YZ4AUWL.js → chunk-EWR23OA4.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-B2YKAP2I.js} +2 -2
- package/dist/lib/cubing/chunks/{twisty-dynamic-3d-XQOH6E2O.js → twisty-dynamic-3d-SLKSUJTE.js} +1804 -192
- package/dist/lib/cubing/chunks/twisty-dynamic-3d-SLKSUJTE.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-EWR23OA4.js.map} +0 -0
- /package/dist/lib/cubing/chunks/{chunk-GP66KY36.js.map → chunk-B2YKAP2I.js.map} +0 -0
package/dist/lib/cubing/chunks/{twisty-dynamic-3d-XQOH6E2O.js → twisty-dynamic-3d-SLKSUJTE.js}
RENAMED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
bulk3DCode,
|
|
4
4
|
haveStartedSharingRenderers,
|
|
5
5
|
hintFaceletStyles
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-B2YKAP2I.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
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";
|
|
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,56 +35,339 @@ 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 normals = [];
|
|
91
|
+
const indices = [];
|
|
92
|
+
const geometry = new BufferGeometry();
|
|
93
|
+
const point = new Vector3();
|
|
94
|
+
const core = new Vector3();
|
|
95
|
+
const offset = new Vector3();
|
|
96
|
+
const u = new Vector3();
|
|
97
|
+
const v = new Vector3();
|
|
98
|
+
const radii = new Vector3();
|
|
99
|
+
const outerness = [0, 0, 0];
|
|
100
|
+
const radiiAt = (p, out) => {
|
|
101
|
+
const towardCorner = (Math.abs(p.x) * Math.abs(p.y) * Math.abs(p.z) / (halfSize * halfSize * halfSize)) ** cornerSharpness;
|
|
102
|
+
let outwardFaceCount = 0;
|
|
103
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
104
|
+
const towardPositive = (component(p, axis) / halfSize + 1) / 2;
|
|
105
|
+
outerness[axis] = towardPositive * outward[axis][1] + (1 - towardPositive) * outward[axis][0];
|
|
106
|
+
outwardFaceCount += outerness[axis];
|
|
107
|
+
}
|
|
108
|
+
const interiorCorner = Math.min(Math.max(2 - outwardFaceCount, 0), 1);
|
|
109
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
110
|
+
const alongEdge = innerEdgeRadius + (outerAxisRadius - innerEdgeRadius) * outerness[axis];
|
|
111
|
+
const atCorner = innerCornerRadius + (outerAxisRadius - innerCornerRadius) * outerness[axis];
|
|
112
|
+
setComponent(
|
|
113
|
+
out,
|
|
114
|
+
axis,
|
|
115
|
+
alongEdge + (atCorner - alongEdge) * towardCorner * interiorCorner
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
for (let faceIdx = 0; faceIdx < faceNormals.length; faceIdx++) {
|
|
120
|
+
const normal2 = faceNormals[faceIdx];
|
|
121
|
+
u.set(normal2.y, normal2.z, normal2.x);
|
|
122
|
+
v.crossVectors(normal2, u);
|
|
123
|
+
const vertexStart = positions.length / 3;
|
|
124
|
+
for (let i = 0; i < gridSize; i++) {
|
|
125
|
+
for (let j = 0; j < gridSize; j++) {
|
|
126
|
+
point.copy(u).multiplyScalar(samples[i]).addScaledVector(v, samples[j]).addScaledVector(normal2, halfSize);
|
|
127
|
+
radiiAt(point, radii);
|
|
128
|
+
core.set(
|
|
129
|
+
clamp(point.x, halfSize - radii.x),
|
|
130
|
+
clamp(point.y, halfSize - radii.y),
|
|
131
|
+
clamp(point.z, halfSize - radii.z)
|
|
132
|
+
);
|
|
133
|
+
offset.set(
|
|
134
|
+
(point.x - core.x) / radii.x,
|
|
135
|
+
(point.y - core.y) / radii.y,
|
|
136
|
+
(point.z - core.z) / radii.z
|
|
137
|
+
).normalize();
|
|
138
|
+
positions.push(
|
|
139
|
+
core.x + offset.x * radii.x,
|
|
140
|
+
core.y + offset.y * radii.y,
|
|
141
|
+
core.z + offset.z * radii.z
|
|
142
|
+
);
|
|
143
|
+
normals.push(
|
|
144
|
+
...offset.set(offset.x / radii.x, offset.y / radii.y, offset.z / radii.z).normalize().toArray()
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const indexStart = indices.length;
|
|
149
|
+
for (let i = 0; i < gridSize - 1; i++) {
|
|
150
|
+
for (let j = 0; j < gridSize - 1; j++) {
|
|
151
|
+
const a = vertexStart + i * gridSize + j;
|
|
152
|
+
const b = a + gridSize;
|
|
153
|
+
indices.push(a, b, b + 1, a, b + 1, a + 1);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
geometry.addGroup(indexStart, indices.length - indexStart, faceIdx);
|
|
157
|
+
}
|
|
158
|
+
geometry.setAttribute(
|
|
159
|
+
"position",
|
|
160
|
+
new BufferAttribute(new Float32Array(positions), 3)
|
|
161
|
+
);
|
|
162
|
+
geometry.setAttribute(
|
|
163
|
+
"normal",
|
|
164
|
+
new BufferAttribute(new Float32Array(normals), 3)
|
|
165
|
+
);
|
|
166
|
+
geometry.setIndex(indices);
|
|
167
|
+
return geometry;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/cubing/twisty/views/3D/puzzles/CubieStyle.ts
|
|
171
|
+
import { AmbientLight } from "three/src/lights/AmbientLight.js";
|
|
172
|
+
import { DirectionalLight } from "three/src/lights/DirectionalLight.js";
|
|
173
|
+
import { MeshPhongMaterial } from "three/src/materials/MeshPhongMaterial.js";
|
|
174
|
+
import { Color } from "three/src/math/Color.js";
|
|
175
|
+
import { Euler } from "three/src/math/Euler.js";
|
|
176
|
+
import { Vector3 as Vector32 } from "three/src/math/Vector3.js";
|
|
177
|
+
var cubeFaceStyles = [
|
|
178
|
+
{
|
|
179
|
+
vector: new Vector32(0, 1, 0),
|
|
180
|
+
fromZ: new Euler(-TAU / 4, 0, 0),
|
|
181
|
+
color: 16777215,
|
|
182
|
+
dimColor: 14540253,
|
|
183
|
+
hintColor: 16777215,
|
|
184
|
+
hintDimColor: 14540253,
|
|
185
|
+
hintOpacityScale: 1.25
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
vector: new Vector32(-1, 0, 0),
|
|
189
|
+
fromZ: new Euler(0, -TAU / 4, 0),
|
|
190
|
+
color: 16750848,
|
|
191
|
+
dimColor: 8934656,
|
|
192
|
+
hintColor: 16750848,
|
|
193
|
+
hintDimColor: 8930304,
|
|
194
|
+
hintOpacityScale: 1
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
vector: new Vector32(0, 0, 1),
|
|
198
|
+
fromZ: new Euler(0, 0, 0),
|
|
199
|
+
color: 65280,
|
|
200
|
+
dimColor: 34816,
|
|
201
|
+
hintColor: 65280,
|
|
202
|
+
hintDimColor: 39168,
|
|
203
|
+
hintOpacityScale: 1
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
vector: new Vector32(1, 0, 0),
|
|
207
|
+
fromZ: new Euler(0, TAU / 4, 0),
|
|
208
|
+
color: 16711680,
|
|
209
|
+
dimColor: 6684672,
|
|
210
|
+
hintColor: 16711680,
|
|
211
|
+
hintDimColor: 6684672,
|
|
212
|
+
hintOpacityScale: 1
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
vector: new Vector32(0, 0, -1),
|
|
216
|
+
fromZ: new Euler(0, TAU / 2, 0),
|
|
217
|
+
color: 2254591,
|
|
218
|
+
dimColor: 1127304,
|
|
219
|
+
hintColor: 2254591,
|
|
220
|
+
hintDimColor: 6246,
|
|
221
|
+
hintOpacityScale: 0.75
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
vector: new Vector32(0, -1, 0),
|
|
225
|
+
fromZ: new Euler(TAU / 4, 0, 0),
|
|
226
|
+
color: 16776960,
|
|
227
|
+
dimColor: 8947712,
|
|
228
|
+
hintColor: 16776960,
|
|
229
|
+
hintDimColor: 14540032,
|
|
230
|
+
hintOpacityScale: 1.25
|
|
231
|
+
}
|
|
232
|
+
];
|
|
233
|
+
var cubieBodyDimensions = {
|
|
234
|
+
/**
|
|
235
|
+
* Half-width of a `stickerless` cubie body. Deliberately more than the 0.5
|
|
236
|
+
* that would make pieces exactly fill their slot: oversized pieces press into
|
|
237
|
+
* each other, which buries most of each rounded edge inside its neighbor and
|
|
238
|
+
* keeps the dividing lines thin.
|
|
239
|
+
*/
|
|
240
|
+
halfWidth: 0.54,
|
|
241
|
+
/**
|
|
242
|
+
* Roll at the rim of a facelet, along the axis the facelet faces — so also
|
|
243
|
+
* the rounding of the puzzle's own outer edges and corners. Effectively zero,
|
|
244
|
+
* which leaves those edges sharp and the plates dead flat.
|
|
245
|
+
*
|
|
246
|
+
* Not exactly zero: the construction lifts each face off the core box along
|
|
247
|
+
* this axis, so a true zero leaves nothing to normalize in the middle of a
|
|
248
|
+
* face. A few thousandths is below a pixel at any sane size.
|
|
249
|
+
*/
|
|
250
|
+
outerAxisRadius: 4e-3,
|
|
251
|
+
/**
|
|
252
|
+
* Rounding along an axis pointing at a neighboring piece, in the middle of an
|
|
253
|
+
* edge. Sets how wide the dividing line between two pieces reads.
|
|
254
|
+
*/
|
|
255
|
+
innerEdgeRadius: 0.07,
|
|
256
|
+
/**
|
|
257
|
+
* The same axis at a corner. This one rounds a facelet's corners within its
|
|
258
|
+
* own plane, so it is what turns a center into a disc, and it costs no
|
|
259
|
+
* thickness because the roll stays `outerAxisRadius` deep.
|
|
260
|
+
*/
|
|
261
|
+
innerCornerRadius: 0.34,
|
|
262
|
+
/** How tightly the corner rounding is pulled in toward the corners. */
|
|
263
|
+
cornerSharpness: 1.3,
|
|
264
|
+
/**
|
|
265
|
+
* Everything above, shrunk about each piece's own center. Scaling rather than
|
|
266
|
+
* trimming the half-width leaves every proportion of the piece untouched and
|
|
267
|
+
* only opens a gap against its neighbors, so the pieces read as separate with
|
|
268
|
+
* a thin line of the puzzle's interior showing between them.
|
|
269
|
+
*/
|
|
270
|
+
pieceScale: 0.95,
|
|
271
|
+
roundingSegments: 7
|
|
272
|
+
};
|
|
273
|
+
function cubieBodyHalfExtent(layers) {
|
|
274
|
+
return layers / 2 - 0.5 + cubieBodyDimensions.halfWidth * cubieBodyDimensions.pieceScale;
|
|
275
|
+
}
|
|
276
|
+
var BODY_SHININESS = 30;
|
|
277
|
+
var BODY_SPECULAR = 657930;
|
|
278
|
+
function newBodyMaterial(color) {
|
|
279
|
+
return new MeshPhongMaterial({
|
|
280
|
+
color: typeof color === "number" ? new Color(color).convertLinearToSRGB() : color,
|
|
281
|
+
shininess: BODY_SHININESS,
|
|
282
|
+
specular: new Color(BODY_SPECULAR).convertLinearToSRGB()
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
function newVertexColorBodyMaterial() {
|
|
286
|
+
const material = newBodyMaterial(16777215);
|
|
287
|
+
material.vertexColors = true;
|
|
288
|
+
return material;
|
|
289
|
+
}
|
|
290
|
+
var bodyMaskColors = {
|
|
291
|
+
/** The plastic that shows through the grooves between pieces. */
|
|
292
|
+
internal: 921102,
|
|
293
|
+
ignored: 6710886,
|
|
294
|
+
oriented: 4513228,
|
|
295
|
+
experimentalOriented2: 16776618,
|
|
296
|
+
mystery: 15911883
|
|
297
|
+
};
|
|
298
|
+
var hintMaskStyles = {
|
|
299
|
+
ignored: { color: 13421772, opacity: 0.75 },
|
|
300
|
+
oriented: { color: 4513228, opacity: 0.5 },
|
|
301
|
+
experimentalOriented2: { color: 16775545, opacity: 0.5 },
|
|
302
|
+
mystery: { color: 15911883, opacity: 0.5 }
|
|
303
|
+
};
|
|
304
|
+
var AMBIENT_LIGHT_INTENSITY = 2.04;
|
|
305
|
+
var KEY_LIGHT_INTENSITY = 1.55;
|
|
306
|
+
var FILL_LIGHT_INTENSITY = 0.3;
|
|
307
|
+
var RIM_LIGHT_INTENSITY = 0.3;
|
|
308
|
+
function addCubieBodyLighting(target) {
|
|
309
|
+
target.add(new AmbientLight(16777215, AMBIENT_LIGHT_INTENSITY));
|
|
310
|
+
const keyLight = new DirectionalLight(16777215, KEY_LIGHT_INTENSITY);
|
|
311
|
+
keyLight.position.set(3, 5, 4);
|
|
312
|
+
target.add(keyLight);
|
|
313
|
+
const fillLight = new DirectionalLight(16777215, FILL_LIGHT_INTENSITY);
|
|
314
|
+
fillLight.position.set(-5, 2, 3);
|
|
315
|
+
target.add(fillLight);
|
|
316
|
+
const rimLight = new DirectionalLight(16777215, RIM_LIGHT_INTENSITY);
|
|
317
|
+
rimLight.position.set(-3, -2, -4);
|
|
318
|
+
target.add(rimLight);
|
|
319
|
+
}
|
|
320
|
+
|
|
39
321
|
// src/cubing/twisty/views/3D/puzzles/Cube3D.ts
|
|
40
322
|
var svgLoader = new TextureLoader();
|
|
323
|
+
function newHintMaterial(style) {
|
|
324
|
+
return new MeshBasicMaterial({
|
|
325
|
+
color: new Color2(style.color).convertLinearToSRGB(),
|
|
326
|
+
side: BackSide,
|
|
327
|
+
transparent: true,
|
|
328
|
+
opacity: style.opacity
|
|
329
|
+
});
|
|
330
|
+
}
|
|
41
331
|
var ignoredMaterial = new MeshBasicMaterial({
|
|
42
|
-
color: new
|
|
43
|
-
});
|
|
44
|
-
var ignoredMaterialHint = new MeshBasicMaterial({
|
|
45
|
-
color: new Color(13421772).convertLinearToSRGB(),
|
|
46
|
-
side: BackSide,
|
|
47
|
-
transparent: true,
|
|
48
|
-
opacity: 0.75
|
|
332
|
+
color: new Color2(bodyMaskColors.ignored).convertLinearToSRGB()
|
|
49
333
|
});
|
|
334
|
+
var ignoredMaterialHint = newHintMaterial(hintMaskStyles.ignored);
|
|
50
335
|
var invisibleMaterial = new MeshBasicMaterial({
|
|
51
336
|
visible: false
|
|
52
337
|
});
|
|
53
338
|
var orientedMaterial = new MeshBasicMaterial({
|
|
54
|
-
color:
|
|
55
|
-
});
|
|
56
|
-
var orientedMaterialHint = new MeshBasicMaterial({
|
|
57
|
-
color: 4513228,
|
|
58
|
-
side: BackSide,
|
|
59
|
-
transparent: true,
|
|
60
|
-
opacity: 0.5
|
|
339
|
+
color: bodyMaskColors.oriented
|
|
61
340
|
});
|
|
341
|
+
var orientedMaterialHint = newHintMaterial(hintMaskStyles.oriented);
|
|
62
342
|
var experimentalOriented2Material = new MeshBasicMaterial({
|
|
63
|
-
color:
|
|
64
|
-
});
|
|
65
|
-
var experimentalOriented2MaterialHint = new MeshBasicMaterial({
|
|
66
|
-
color: 16775545,
|
|
67
|
-
side: BackSide,
|
|
68
|
-
transparent: true,
|
|
69
|
-
opacity: 0.5
|
|
343
|
+
color: bodyMaskColors.experimentalOriented2
|
|
70
344
|
});
|
|
345
|
+
var experimentalOriented2MaterialHint = newHintMaterial(
|
|
346
|
+
hintMaskStyles.experimentalOriented2
|
|
347
|
+
);
|
|
71
348
|
var mysteryMaterial = new MeshBasicMaterial({
|
|
72
|
-
color:
|
|
73
|
-
});
|
|
74
|
-
var mysterMaterialHint = new MeshBasicMaterial({
|
|
75
|
-
color: 15911883,
|
|
76
|
-
side: BackSide,
|
|
77
|
-
transparent: true,
|
|
78
|
-
opacity: 0.5
|
|
349
|
+
color: bodyMaskColors.mystery
|
|
79
350
|
});
|
|
351
|
+
var mysterMaterialHint = newHintMaterial(hintMaskStyles.mystery);
|
|
352
|
+
var internalBodyMaterial = newBodyMaterial(bodyMaskColors.internal);
|
|
353
|
+
var ignoredBodyMaterial = newBodyMaterial(bodyMaskColors.ignored);
|
|
354
|
+
var orientedBodyMaterial = newBodyMaterial(bodyMaskColors.oriented);
|
|
355
|
+
var experimentalOriented2BodyMaterial = newBodyMaterial(
|
|
356
|
+
bodyMaskColors.experimentalOriented2
|
|
357
|
+
);
|
|
358
|
+
var mysteryBodyMaterial = newBodyMaterial(bodyMaskColors.mystery);
|
|
80
359
|
var AxisInfo = class {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
360
|
+
vector;
|
|
361
|
+
fromZ;
|
|
362
|
+
stickerMaterial;
|
|
363
|
+
hintStickerMaterial;
|
|
364
|
+
bodyMaterial;
|
|
365
|
+
constructor(style) {
|
|
366
|
+
const { color, dimColor, hintOpacityScale } = style;
|
|
367
|
+
this.vector = style.vector;
|
|
368
|
+
this.fromZ = style.fromZ;
|
|
369
|
+
const colorLinearSRGB = new Color2(color).convertLinearToSRGB();
|
|
370
|
+
const dimColorLinearSRGB = new Color2(dimColor).convertLinearToSRGB();
|
|
89
371
|
this.stickerMaterial = {
|
|
90
372
|
regular: new MeshBasicMaterial({
|
|
91
373
|
color: colorLinearSRGB,
|
|
@@ -104,18 +386,12 @@ var AxisInfo = class {
|
|
|
104
386
|
mystery: mysteryMaterial
|
|
105
387
|
};
|
|
106
388
|
this.hintStickerMaterial = {
|
|
107
|
-
regular:
|
|
108
|
-
color:
|
|
109
|
-
side: BackSide,
|
|
110
|
-
transparent: true,
|
|
389
|
+
regular: newHintMaterial({
|
|
390
|
+
color: style.hintColor,
|
|
111
391
|
opacity: 0.5 * hintOpacityScale
|
|
112
392
|
}),
|
|
113
|
-
dim:
|
|
114
|
-
color:
|
|
115
|
-
options?.hintDimColor ?? dimColor
|
|
116
|
-
).convertLinearToSRGB(),
|
|
117
|
-
side: BackSide,
|
|
118
|
-
transparent: true,
|
|
393
|
+
dim: newHintMaterial({
|
|
394
|
+
color: style.hintDimColor,
|
|
119
395
|
opacity: 0.5 * hintOpacityScale
|
|
120
396
|
}),
|
|
121
397
|
oriented: orientedMaterialHint,
|
|
@@ -124,64 +400,20 @@ var AxisInfo = class {
|
|
|
124
400
|
invisible: invisibleMaterial,
|
|
125
401
|
mystery: mysterMaterialHint
|
|
126
402
|
};
|
|
403
|
+
this.bodyMaterial = {
|
|
404
|
+
regular: newBodyMaterial(colorLinearSRGB),
|
|
405
|
+
dim: newBodyMaterial(dimColorLinearSRGB),
|
|
406
|
+
oriented: orientedBodyMaterial,
|
|
407
|
+
experimentalOriented2: experimentalOriented2BodyMaterial,
|
|
408
|
+
ignored: ignoredBodyMaterial,
|
|
409
|
+
// A solid piece of plastic can't have a hole punched in it, so an
|
|
410
|
+
// invisible facelet falls back to the internal plastic color.
|
|
411
|
+
invisible: internalBodyMaterial,
|
|
412
|
+
mystery: mysteryBodyMaterial
|
|
413
|
+
};
|
|
127
414
|
}
|
|
128
|
-
vector;
|
|
129
|
-
fromZ;
|
|
130
|
-
color;
|
|
131
|
-
dimColor;
|
|
132
|
-
hintOpacityScale;
|
|
133
|
-
stickerMaterial;
|
|
134
|
-
hintStickerMaterial;
|
|
135
415
|
};
|
|
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
|
-
];
|
|
416
|
+
var axesInfo = cubeFaceStyles.map((style) => new AxisInfo(style));
|
|
185
417
|
var face = {
|
|
186
418
|
U: 0,
|
|
187
419
|
L: 1,
|
|
@@ -226,11 +458,19 @@ var cubieDimensions = {
|
|
|
226
458
|
// stickerWidth: 0.85, // Now `faceletScale` in options.
|
|
227
459
|
stickerElevation: 0.503,
|
|
228
460
|
foundationWidth: 1,
|
|
229
|
-
defaultHintStickerElevation: 1.45
|
|
461
|
+
defaultHintStickerElevation: 1.45,
|
|
462
|
+
/**
|
|
463
|
+
* How far back the eight corners of a `stickerless` cubie are shaved, from 0
|
|
464
|
+
* (not at all) to 1 (all the way back to the edges). This is the only knob
|
|
465
|
+
* that widens the notch where four pieces meet without also widening the
|
|
466
|
+
* straight dividing lines.
|
|
467
|
+
*/
|
|
468
|
+
bodyVertexCut: 0.45
|
|
230
469
|
};
|
|
231
470
|
var EXPERIMENTAL_PICTURE_CUBE_HINT_ELEVATION = 2;
|
|
232
471
|
var cube3DOptionsDefaults = {
|
|
233
472
|
showMainStickers: true,
|
|
473
|
+
experimentalCubieStyle: "stickerless",
|
|
234
474
|
hintFacelets: "floating",
|
|
235
475
|
showFoundation: true,
|
|
236
476
|
experimentalStickeringMask: void 0,
|
|
@@ -275,18 +515,18 @@ function t(v, t4) {
|
|
|
275
515
|
return new Quaternion().setFromAxisAngle(v, TAU * t4 / 4);
|
|
276
516
|
}
|
|
277
517
|
var r = {
|
|
278
|
-
O: new
|
|
279
|
-
U: new
|
|
280
|
-
L: new
|
|
281
|
-
F: new
|
|
282
|
-
R: new
|
|
283
|
-
B: new
|
|
284
|
-
D: new
|
|
518
|
+
O: new Vector33(0, 0, 0),
|
|
519
|
+
U: new Vector33(0, -1, 0),
|
|
520
|
+
L: new Vector33(1, 0, 0),
|
|
521
|
+
F: new Vector33(0, 0, -1),
|
|
522
|
+
R: new Vector33(-1, 0, 0),
|
|
523
|
+
B: new Vector33(0, 0, 1),
|
|
524
|
+
D: new Vector33(0, 1, 0)
|
|
285
525
|
};
|
|
286
526
|
var firstPiecePosition = {
|
|
287
|
-
EDGES: new
|
|
288
|
-
CORNERS: new
|
|
289
|
-
CENTERS: new
|
|
527
|
+
EDGES: new Vector33(0, 1, 1),
|
|
528
|
+
CORNERS: new Vector33(1, 1, 1),
|
|
529
|
+
CENTERS: new Vector33(0, 1, 0)
|
|
290
530
|
};
|
|
291
531
|
var orientationRotation = {
|
|
292
532
|
EDGES: [0, 1].map(
|
|
@@ -344,6 +584,9 @@ var pieceDefs = {
|
|
|
344
584
|
]
|
|
345
585
|
};
|
|
346
586
|
var CUBE_SCALE = 1 / 3;
|
|
587
|
+
function cubeScale(stickerless) {
|
|
588
|
+
return stickerless ? 0.5 / cubieBodyHalfExtent(3) : CUBE_SCALE;
|
|
589
|
+
}
|
|
347
590
|
var pictureStickerCoords = {
|
|
348
591
|
EDGES: [
|
|
349
592
|
[
|
|
@@ -455,11 +698,11 @@ function sharedCubieFoundationGeometry() {
|
|
|
455
698
|
));
|
|
456
699
|
}
|
|
457
700
|
function newStickerGeometry() {
|
|
458
|
-
const r2 = new
|
|
701
|
+
const r2 = new BufferGeometry2();
|
|
459
702
|
const half = 0.5;
|
|
460
703
|
r2.setAttribute(
|
|
461
704
|
"position",
|
|
462
|
-
new
|
|
705
|
+
new BufferAttribute2(
|
|
463
706
|
new Float32Array([
|
|
464
707
|
half,
|
|
465
708
|
half,
|
|
@@ -485,7 +728,7 @@ function newStickerGeometry() {
|
|
|
485
728
|
);
|
|
486
729
|
r2.setAttribute(
|
|
487
730
|
"uv",
|
|
488
|
-
new
|
|
731
|
+
new BufferAttribute2(
|
|
489
732
|
new Float32Array([
|
|
490
733
|
1,
|
|
491
734
|
1,
|
|
@@ -517,6 +760,26 @@ function newStickerGeometry() {
|
|
|
517
760
|
);
|
|
518
761
|
return r2;
|
|
519
762
|
}
|
|
763
|
+
var cubieBodyGeometryCache = /* @__PURE__ */ new Map();
|
|
764
|
+
function cubieBodyGeometry(orbit, outerAxes) {
|
|
765
|
+
const cached = cubieBodyGeometryCache.get(orbit);
|
|
766
|
+
if (cached) {
|
|
767
|
+
return cached;
|
|
768
|
+
}
|
|
769
|
+
const scale = cubieBodyDimensions.pieceScale;
|
|
770
|
+
const geometry = beveledCubieGeometry(
|
|
771
|
+
axesInfo.map((axisInfo) => axisInfo.vector),
|
|
772
|
+
outerAxes,
|
|
773
|
+
cubieBodyDimensions.halfWidth * scale,
|
|
774
|
+
cubieBodyDimensions.outerAxisRadius * scale,
|
|
775
|
+
cubieBodyDimensions.innerEdgeRadius * scale,
|
|
776
|
+
cubieBodyDimensions.innerCornerRadius * scale,
|
|
777
|
+
cubieBodyDimensions.cornerSharpness,
|
|
778
|
+
cubieBodyDimensions.roundingSegments
|
|
779
|
+
);
|
|
780
|
+
cubieBodyGeometryCache.set(orbit, geometry);
|
|
781
|
+
return geometry;
|
|
782
|
+
}
|
|
520
783
|
var sharedStickerGeometryCache;
|
|
521
784
|
function sharedStickerGeometry() {
|
|
522
785
|
return sharedStickerGeometryCache ??= newStickerGeometry();
|
|
@@ -547,7 +810,11 @@ var Cube3D = class extends Object3D {
|
|
|
547
810
|
this.createCubie.bind(this, orbit, orbitFaceletInfo)
|
|
548
811
|
);
|
|
549
812
|
}
|
|
550
|
-
this
|
|
813
|
+
if (this.#stickerless()) {
|
|
814
|
+
addCubieBodyLighting(this);
|
|
815
|
+
}
|
|
816
|
+
const scale = cubeScale(this.#stickerless());
|
|
817
|
+
this.scale.set(scale, scale, scale);
|
|
551
818
|
if (this.options.experimentalStickeringMask) {
|
|
552
819
|
this.setStickeringMask(this.options.experimentalStickeringMask);
|
|
553
820
|
}
|
|
@@ -654,12 +921,24 @@ var Cube3D = class extends Object3D {
|
|
|
654
921
|
experimentalSetHintStickerSpriteURL(hintStickerSpriteURL) {
|
|
655
922
|
this.#setHintSpriteURL?.(hintStickerSpriteURL);
|
|
656
923
|
}
|
|
924
|
+
#stickerless() {
|
|
925
|
+
return this.options.experimentalCubieStyle === "stickerless";
|
|
926
|
+
}
|
|
927
|
+
#setFaceletMaterial(faceletInfo, mask) {
|
|
928
|
+
const axisInfo = axesInfo[faceletInfo.faceIdx];
|
|
929
|
+
const { bodyMaterialIndex } = faceletInfo;
|
|
930
|
+
if (bodyMaterialIndex === void 0) {
|
|
931
|
+
faceletInfo.facelet.material = axisInfo.stickerMaterial[mask];
|
|
932
|
+
} else {
|
|
933
|
+
faceletInfo.facelet.material[bodyMaterialIndex] = axisInfo.bodyMaterial[mask];
|
|
934
|
+
}
|
|
935
|
+
}
|
|
657
936
|
setStickeringMask(stickeringMask) {
|
|
658
937
|
if (stickeringMask.specialBehaviour === "picture") {
|
|
659
938
|
for (const pieceInfos of Object.values(this.kpuzzleFaceletInfo)) {
|
|
660
939
|
for (const faceletInfos of pieceInfos) {
|
|
661
940
|
for (const faceletInfo of faceletInfos) {
|
|
662
|
-
faceletInfo
|
|
941
|
+
this.#setFaceletMaterial(faceletInfo, "invisible");
|
|
663
942
|
const { hintFacelet } = faceletInfo;
|
|
664
943
|
if (hintFacelet) {
|
|
665
944
|
hintFacelet.material = invisibleMaterial;
|
|
@@ -682,7 +961,7 @@ var Cube3D = class extends Object3D {
|
|
|
682
961
|
if (faceletStickeringMask) {
|
|
683
962
|
const faceletInfo = pieceInfo[faceletIdx];
|
|
684
963
|
const stickeringMask2 = typeof faceletStickeringMask === "string" ? faceletStickeringMask : faceletStickeringMask?.mask;
|
|
685
|
-
faceletInfo
|
|
964
|
+
this.#setFaceletMaterial(faceletInfo, stickeringMask2);
|
|
686
965
|
const hintStickeringMask = typeof faceletStickeringMask === "string" ? stickeringMask2 : faceletStickeringMask.hintMask ?? stickeringMask2;
|
|
687
966
|
if (faceletInfo.hintFacelet) {
|
|
688
967
|
faceletInfo.hintFacelet.material = axesInfo[faceletInfo.faceIdx].hintStickerMaterial[hintStickeringMask];
|
|
@@ -769,26 +1048,39 @@ var Cube3D = class extends Object3D {
|
|
|
769
1048
|
const cubieFaceletInfo = [];
|
|
770
1049
|
orbitFacelets.push(cubieFaceletInfo);
|
|
771
1050
|
const cubie = new Group();
|
|
772
|
-
|
|
1051
|
+
const body = this.#stickerless() ? new Mesh(
|
|
1052
|
+
cubieBodyGeometry(
|
|
1053
|
+
orbit,
|
|
1054
|
+
cubieStickerOrder.slice(0, piece.stickerFaces.length)
|
|
1055
|
+
),
|
|
1056
|
+
axesInfo.map(() => internalBodyMaterial)
|
|
1057
|
+
) : null;
|
|
1058
|
+
if (body) {
|
|
1059
|
+
cubie.add(body);
|
|
1060
|
+
} else if (this.options.showFoundation) {
|
|
773
1061
|
const foundation = this.createCubieFoundation();
|
|
774
1062
|
cubie.add(foundation);
|
|
775
1063
|
this.experimentalFoundationMeshes.push(foundation);
|
|
776
1064
|
}
|
|
777
1065
|
for (let i = 0; i < piece.stickerFaces.length; i++) {
|
|
778
|
-
const
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
1066
|
+
const faceIdx = piece.stickerFaces[i];
|
|
1067
|
+
const faceletInfo = body ? { faceIdx, facelet: body, bodyMaterialIndex: cubieStickerOrder[i] } : {
|
|
1068
|
+
faceIdx,
|
|
1069
|
+
facelet: this.createSticker(
|
|
1070
|
+
axesInfo[cubieStickerOrder[i]],
|
|
1071
|
+
axesInfo[faceIdx],
|
|
1072
|
+
false
|
|
1073
|
+
)
|
|
786
1074
|
};
|
|
787
|
-
|
|
1075
|
+
if (body) {
|
|
1076
|
+
body.material[cubieStickerOrder[i]] = axesInfo[faceIdx].bodyMaterial.regular;
|
|
1077
|
+
} else {
|
|
1078
|
+
cubie.add(faceletInfo.facelet);
|
|
1079
|
+
}
|
|
788
1080
|
if (this.options.hintFacelets === "floating") {
|
|
789
1081
|
const hintSticker = this.createSticker(
|
|
790
1082
|
axesInfo[cubieStickerOrder[i]],
|
|
791
|
-
axesInfo[
|
|
1083
|
+
axesInfo[faceIdx],
|
|
792
1084
|
true
|
|
793
1085
|
);
|
|
794
1086
|
cubie.add(hintSticker);
|
|
@@ -834,7 +1126,7 @@ var Cube3D = class extends Object3D {
|
|
|
834
1126
|
}
|
|
835
1127
|
mesh.geometry.setAttribute(
|
|
836
1128
|
"uv",
|
|
837
|
-
new
|
|
1129
|
+
new BufferAttribute2(
|
|
838
1130
|
new Float32Array([
|
|
839
1131
|
v3.x,
|
|
840
1132
|
v3.y,
|
|
@@ -904,6 +1196,9 @@ var Cube3D = class extends Object3D {
|
|
|
904
1196
|
}
|
|
905
1197
|
/** @deprecated */
|
|
906
1198
|
experimentalSetFoundationOpacity(opacity) {
|
|
1199
|
+
if (this.experimentalFoundationMeshes.length === 0) {
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
907
1202
|
this.experimentalFoundationMeshes[0].material.opacity = opacity;
|
|
908
1203
|
}
|
|
909
1204
|
/** @deprecated */
|
|
@@ -913,8 +1208,10 @@ var Cube3D = class extends Object3D {
|
|
|
913
1208
|
for (const pieceInfo of orbitInfo) {
|
|
914
1209
|
for (const faceletInfo of pieceInfo) {
|
|
915
1210
|
const scale = getFaceletScale(this.options);
|
|
916
|
-
faceletInfo.
|
|
917
|
-
|
|
1211
|
+
if (faceletInfo.bodyMaterialIndex === void 0) {
|
|
1212
|
+
faceletInfo.facelet.scale.setX(scale);
|
|
1213
|
+
faceletInfo.facelet.scale.setY(scale);
|
|
1214
|
+
}
|
|
918
1215
|
faceletInfo.hintFacelet?.scale.setX(scale);
|
|
919
1216
|
faceletInfo.hintFacelet?.scale.setY(scale);
|
|
920
1217
|
}
|
|
@@ -940,14 +1237,360 @@ var Cube3D = class extends Object3D {
|
|
|
940
1237
|
}
|
|
941
1238
|
};
|
|
942
1239
|
|
|
1240
|
+
// src/cubing/twisty/views/3D/puzzles/CubePieces.ts
|
|
1241
|
+
import { BufferAttribute as BufferAttribute3 } from "three/src/core/BufferAttribute.js";
|
|
1242
|
+
import { BufferGeometry as BufferGeometry3 } from "three/src/core/BufferGeometry.js";
|
|
1243
|
+
import { Matrix4 as Matrix42 } from "three/src/math/Matrix4.js";
|
|
1244
|
+
import { Vector3 as Vector34 } from "three/src/math/Vector3.js";
|
|
1245
|
+
|
|
1246
|
+
// src/cubing/twisty/views/3D/puzzles/StickerlessPlan.ts
|
|
1247
|
+
var HINT_FACELET_ELEVATION = 0.5;
|
|
1248
|
+
var HINT_FACELET_SCALE = 0.85;
|
|
1249
|
+
var BODY_MATERIAL_INDEX = 0;
|
|
1250
|
+
var HINT_MATERIAL_INDEX = 1;
|
|
1251
|
+
var faceletMeshStickeringMasks = [
|
|
1252
|
+
"regular",
|
|
1253
|
+
"dim",
|
|
1254
|
+
"oriented",
|
|
1255
|
+
"experimentalOriented2",
|
|
1256
|
+
"ignored",
|
|
1257
|
+
"invisible",
|
|
1258
|
+
"mystery"
|
|
1259
|
+
];
|
|
1260
|
+
function bodyColor(style, mask) {
|
|
1261
|
+
switch (mask) {
|
|
1262
|
+
case "regular":
|
|
1263
|
+
return style.color;
|
|
1264
|
+
case "dim":
|
|
1265
|
+
return style.dimColor;
|
|
1266
|
+
// A solid piece of plastic can't have a hole punched in it, so an invisible
|
|
1267
|
+
// facelet falls back to the internal plastic color.
|
|
1268
|
+
case "invisible":
|
|
1269
|
+
return bodyMaskColors.internal;
|
|
1270
|
+
default:
|
|
1271
|
+
return bodyMaskColors[mask];
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
function hintColor(style, mask) {
|
|
1275
|
+
switch (mask) {
|
|
1276
|
+
case "regular":
|
|
1277
|
+
return { color: style.hintColor, opacity: 0.5 * style.hintOpacityScale };
|
|
1278
|
+
case "dim":
|
|
1279
|
+
return {
|
|
1280
|
+
color: style.hintDimColor,
|
|
1281
|
+
opacity: 0.5 * style.hintOpacityScale
|
|
1282
|
+
};
|
|
1283
|
+
case "invisible":
|
|
1284
|
+
return { color: 0, opacity: 0 };
|
|
1285
|
+
default:
|
|
1286
|
+
return hintMaskStyles[mask];
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
function faceletAppearanceKey(faceStyle, mask, hintMask) {
|
|
1290
|
+
return (faceStyle * faceletMeshStickeringMasks.length + faceletMeshStickeringMasks.indexOf(mask)) * faceletMeshStickeringMasks.length + faceletMeshStickeringMasks.indexOf(hintMask);
|
|
1291
|
+
}
|
|
1292
|
+
function faceletAppearance(cache, styles, faceStyle, mask, hintMask) {
|
|
1293
|
+
const key = faceletAppearanceKey(faceStyle, mask, hintMask);
|
|
1294
|
+
const cached = cache.get(key);
|
|
1295
|
+
if (cached) {
|
|
1296
|
+
return cached;
|
|
1297
|
+
}
|
|
1298
|
+
const style = styles[faceStyle];
|
|
1299
|
+
const body = bodyColor(style, mask);
|
|
1300
|
+
const hint = hintColor(style, hintMask);
|
|
1301
|
+
const appearance = new Uint8Array([
|
|
1302
|
+
body >> 16 & 255,
|
|
1303
|
+
body >> 8 & 255,
|
|
1304
|
+
body & 255,
|
|
1305
|
+
255,
|
|
1306
|
+
hint.color >> 16 & 255,
|
|
1307
|
+
hint.color >> 8 & 255,
|
|
1308
|
+
hint.color & 255,
|
|
1309
|
+
Math.round(hint.opacity * 255)
|
|
1310
|
+
]);
|
|
1311
|
+
cache.set(key, appearance);
|
|
1312
|
+
return appearance;
|
|
1313
|
+
}
|
|
1314
|
+
function writeColor(colors, ranges, appearance, offset = 0) {
|
|
1315
|
+
const array = colors.array;
|
|
1316
|
+
const red = appearance[offset];
|
|
1317
|
+
const green = appearance[offset + 1];
|
|
1318
|
+
const blue = appearance[offset + 2];
|
|
1319
|
+
const alpha = appearance[offset + 3];
|
|
1320
|
+
for (const range of ranges) {
|
|
1321
|
+
for (let vertex = range.start; vertex < range.start + range.count; vertex++) {
|
|
1322
|
+
array[4 * vertex] = red;
|
|
1323
|
+
array[4 * vertex + 1] = green;
|
|
1324
|
+
array[4 * vertex + 2] = blue;
|
|
1325
|
+
array[4 * vertex + 3] = alpha;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
function paintInternal(colors, ranges) {
|
|
1330
|
+
const internal = bodyMaskColors.internal;
|
|
1331
|
+
writeColor(
|
|
1332
|
+
colors,
|
|
1333
|
+
ranges,
|
|
1334
|
+
new Uint8Array([
|
|
1335
|
+
internal >> 16 & 255,
|
|
1336
|
+
internal >> 8 & 255,
|
|
1337
|
+
internal & 255,
|
|
1338
|
+
255
|
|
1339
|
+
])
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
// src/cubing/twisty/views/3D/puzzles/CubePieces.ts
|
|
1344
|
+
var PG_SCALE = 0.5;
|
|
1345
|
+
var FACE_COUNT = 6;
|
|
1346
|
+
function faceIdxForNormal(x, y, z) {
|
|
1347
|
+
for (let faceIdx = 0; faceIdx < FACE_COUNT; faceIdx++) {
|
|
1348
|
+
const { vector } = cubeFaceStyles[faceIdx];
|
|
1349
|
+
if (vector.x * x + vector.y * y + vector.z * z > 0.5) {
|
|
1350
|
+
return faceIdx;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
return -1;
|
|
1354
|
+
}
|
|
1355
|
+
function cubeLayout(stickerDat) {
|
|
1356
|
+
const { stickers } = stickerDat;
|
|
1357
|
+
if (stickerDat.faces.length !== FACE_COUNT || stickers.length === 0) {
|
|
1358
|
+
return null;
|
|
1359
|
+
}
|
|
1360
|
+
let faceletCount = 0;
|
|
1361
|
+
let halfExtent = 0;
|
|
1362
|
+
for (const sticker of stickers) {
|
|
1363
|
+
if (sticker.coords.length !== 12) {
|
|
1364
|
+
return null;
|
|
1365
|
+
}
|
|
1366
|
+
if (!sticker.isDup) {
|
|
1367
|
+
faceletCount++;
|
|
1368
|
+
}
|
|
1369
|
+
for (const coord of sticker.coords) {
|
|
1370
|
+
halfExtent = Math.max(halfExtent, Math.abs(coord));
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
const layers = Math.round(Math.sqrt(faceletCount / FACE_COUNT));
|
|
1374
|
+
if (FACE_COUNT * layers * layers !== faceletCount || layers < 2) {
|
|
1375
|
+
return null;
|
|
1376
|
+
}
|
|
1377
|
+
const slotWidth = 2 * halfExtent / layers;
|
|
1378
|
+
const epsilon = slotWidth / 1e3;
|
|
1379
|
+
const faceColors = new Array(FACE_COUNT);
|
|
1380
|
+
const cubiesByPiece = /* @__PURE__ */ new Map();
|
|
1381
|
+
const cubies = [];
|
|
1382
|
+
const centroid = [0, 0, 0];
|
|
1383
|
+
for (const sticker of stickers) {
|
|
1384
|
+
const { coords } = sticker;
|
|
1385
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
1386
|
+
centroid[axis] = (coords[axis] + coords[axis + 3] + coords[axis + 6] + coords[axis + 9]) / 4;
|
|
1387
|
+
}
|
|
1388
|
+
let normalAxis = 0;
|
|
1389
|
+
for (let axis = 1; axis < 3; axis++) {
|
|
1390
|
+
if (Math.abs(centroid[axis]) > Math.abs(centroid[normalAxis])) {
|
|
1391
|
+
normalAxis = axis;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
const outward = Math.sign(centroid[normalAxis]);
|
|
1395
|
+
const center = new Vector34();
|
|
1396
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
1397
|
+
let expected;
|
|
1398
|
+
if (axis === normalAxis) {
|
|
1399
|
+
expected = outward * halfExtent;
|
|
1400
|
+
center.setComponent(axis, outward * (halfExtent - slotWidth / 2));
|
|
1401
|
+
} else {
|
|
1402
|
+
const slot = Math.round(
|
|
1403
|
+
(centroid[axis] + halfExtent) / slotWidth - 0.5
|
|
1404
|
+
);
|
|
1405
|
+
if (slot < 0 || slot >= layers) {
|
|
1406
|
+
return null;
|
|
1407
|
+
}
|
|
1408
|
+
expected = -halfExtent + (slot + 0.5) * slotWidth;
|
|
1409
|
+
center.setComponent(axis, expected);
|
|
1410
|
+
}
|
|
1411
|
+
if (Math.abs(centroid[axis] - expected) > epsilon) {
|
|
1412
|
+
return null;
|
|
1413
|
+
}
|
|
1414
|
+
const halfSpan = axis === normalAxis ? 0 : slotWidth / 2;
|
|
1415
|
+
for (let vertex = 0; vertex < 4; vertex++) {
|
|
1416
|
+
const offset = Math.abs(coords[3 * vertex + axis] - expected);
|
|
1417
|
+
if (Math.abs(offset - halfSpan) > epsilon) {
|
|
1418
|
+
return null;
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
const faceIdx = faceIdxForNormal(
|
|
1423
|
+
normalAxis === 0 ? outward : 0,
|
|
1424
|
+
normalAxis === 1 ? outward : 0,
|
|
1425
|
+
normalAxis === 2 ? outward : 0
|
|
1426
|
+
);
|
|
1427
|
+
if (faceIdx === -1) {
|
|
1428
|
+
return null;
|
|
1429
|
+
}
|
|
1430
|
+
faceColors[faceIdx] ??= sticker.color;
|
|
1431
|
+
if (faceColors[faceIdx] !== sticker.color) {
|
|
1432
|
+
return null;
|
|
1433
|
+
}
|
|
1434
|
+
const pieceKey = `${sticker.orbit}/${sticker.ord}`;
|
|
1435
|
+
let cubie = cubiesByPiece.get(pieceKey);
|
|
1436
|
+
if (!cubie) {
|
|
1437
|
+
cubie = {
|
|
1438
|
+
orbit: sticker.orbit,
|
|
1439
|
+
ord: sticker.ord,
|
|
1440
|
+
center,
|
|
1441
|
+
outwardFaces: [],
|
|
1442
|
+
stickers: []
|
|
1443
|
+
};
|
|
1444
|
+
cubiesByPiece.set(pieceKey, cubie);
|
|
1445
|
+
cubies.push(cubie);
|
|
1446
|
+
} else if (cubie.center.distanceTo(center) > epsilon) {
|
|
1447
|
+
return null;
|
|
1448
|
+
}
|
|
1449
|
+
if (!cubie.outwardFaces.includes(faceIdx)) {
|
|
1450
|
+
cubie.outwardFaces.push(faceIdx);
|
|
1451
|
+
cubie.outwardFaces.sort((a, b) => a - b);
|
|
1452
|
+
}
|
|
1453
|
+
cubie.stickers.push({ faceIdx, ori: sticker.ori, isDup: !!sticker.isDup });
|
|
1454
|
+
}
|
|
1455
|
+
if (new Set(faceColors).size !== FACE_COUNT) {
|
|
1456
|
+
return null;
|
|
1457
|
+
}
|
|
1458
|
+
return { layers, halfExtent, cubies };
|
|
1459
|
+
}
|
|
1460
|
+
function newCubieShape(outwardFaces, slotWidth) {
|
|
1461
|
+
const scale = cubieBodyDimensions.pieceScale * slotWidth;
|
|
1462
|
+
const body = beveledCubieGeometry(
|
|
1463
|
+
cubeFaceStyles.map((style) => style.vector),
|
|
1464
|
+
outwardFaces,
|
|
1465
|
+
cubieBodyDimensions.halfWidth * scale,
|
|
1466
|
+
cubieBodyDimensions.outerAxisRadius * scale,
|
|
1467
|
+
cubieBodyDimensions.innerEdgeRadius * scale,
|
|
1468
|
+
cubieBodyDimensions.innerCornerRadius * scale,
|
|
1469
|
+
cubieBodyDimensions.cornerSharpness,
|
|
1470
|
+
cubieBodyDimensions.roundingSegments
|
|
1471
|
+
);
|
|
1472
|
+
const bodyPositions = body.getAttribute("position").array;
|
|
1473
|
+
const bodyNormals = body.getAttribute("normal").array;
|
|
1474
|
+
const bodyIndices = Array.from(body.getIndex().array);
|
|
1475
|
+
const faceVertexRanges = [];
|
|
1476
|
+
for (const group of body.groups) {
|
|
1477
|
+
let start = Number.POSITIVE_INFINITY;
|
|
1478
|
+
let end = 0;
|
|
1479
|
+
for (let i = group.start; i < group.start + group.count; i++) {
|
|
1480
|
+
start = Math.min(start, bodyIndices[i]);
|
|
1481
|
+
end = Math.max(end, bodyIndices[i] + 1);
|
|
1482
|
+
}
|
|
1483
|
+
faceVertexRanges[group.materialIndex] = { start, count: end - start };
|
|
1484
|
+
}
|
|
1485
|
+
const positions = Array.from(bodyPositions);
|
|
1486
|
+
const normals = Array.from(bodyNormals);
|
|
1487
|
+
const indices = bodyIndices.slice();
|
|
1488
|
+
const hintVertexRanges = /* @__PURE__ */ new Map();
|
|
1489
|
+
const hintHalfWidth = HINT_FACELET_SCALE * slotWidth / 2;
|
|
1490
|
+
const hintDistance = slotWidth / 2 + HINT_FACELET_ELEVATION;
|
|
1491
|
+
const u = new Vector34();
|
|
1492
|
+
const v = new Vector34();
|
|
1493
|
+
const corner = new Vector34();
|
|
1494
|
+
for (const faceIdx of outwardFaces) {
|
|
1495
|
+
const normal2 = cubeFaceStyles[faceIdx].vector;
|
|
1496
|
+
u.set(normal2.y, normal2.z, normal2.x);
|
|
1497
|
+
v.crossVectors(normal2, u);
|
|
1498
|
+
const start = positions.length / 3;
|
|
1499
|
+
for (const [su, sv] of [
|
|
1500
|
+
[-1, -1],
|
|
1501
|
+
[1, -1],
|
|
1502
|
+
[1, 1],
|
|
1503
|
+
[-1, 1]
|
|
1504
|
+
]) {
|
|
1505
|
+
corner.copy(normal2).multiplyScalar(hintDistance).addScaledVector(u, su * hintHalfWidth).addScaledVector(v, sv * hintHalfWidth);
|
|
1506
|
+
positions.push(corner.x, corner.y, corner.z);
|
|
1507
|
+
normals.push(normal2.x, normal2.y, normal2.z);
|
|
1508
|
+
}
|
|
1509
|
+
indices.push(start, start + 1, start + 2, start, start + 2, start + 3);
|
|
1510
|
+
hintVertexRanges.set(faceIdx, { start, count: 4 });
|
|
1511
|
+
}
|
|
1512
|
+
body.dispose();
|
|
1513
|
+
return {
|
|
1514
|
+
position: new BufferAttribute3(new Float32Array(positions), 3),
|
|
1515
|
+
normal: new BufferAttribute3(new Float32Array(normals), 3),
|
|
1516
|
+
index: new BufferAttribute3(new Uint16Array(indices), 1),
|
|
1517
|
+
vertexCount: positions.length / 3,
|
|
1518
|
+
bodyIndexCount: bodyIndices.length,
|
|
1519
|
+
hintIndexCount: indices.length - bodyIndices.length,
|
|
1520
|
+
faceVertexRanges,
|
|
1521
|
+
hintVertexRanges
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
function newCubiePiece(cubie, shape) {
|
|
1525
|
+
const geometry = new BufferGeometry3();
|
|
1526
|
+
geometry.setAttribute("position", shape.position);
|
|
1527
|
+
geometry.setAttribute("normal", shape.normal);
|
|
1528
|
+
geometry.setIndex(shape.index);
|
|
1529
|
+
const colors = new BufferAttribute3(
|
|
1530
|
+
new Uint8Array(4 * shape.vertexCount),
|
|
1531
|
+
4,
|
|
1532
|
+
true
|
|
1533
|
+
);
|
|
1534
|
+
geometry.setAttribute("color", colors);
|
|
1535
|
+
geometry.addGroup(0, shape.bodyIndexCount, BODY_MATERIAL_INDEX);
|
|
1536
|
+
if (shape.hintIndexCount > 0) {
|
|
1537
|
+
geometry.addGroup(
|
|
1538
|
+
shape.bodyIndexCount,
|
|
1539
|
+
shape.hintIndexCount,
|
|
1540
|
+
HINT_MATERIAL_INDEX
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
paintInternal(colors, shape.faceVertexRanges);
|
|
1544
|
+
const facelets = cubie.stickers.map((sticker) => ({
|
|
1545
|
+
ori: sticker.ori,
|
|
1546
|
+
faceStyle: sticker.faceIdx,
|
|
1547
|
+
// A duplicate shares its square with the real facelet, which is the one
|
|
1548
|
+
// that gets painted.
|
|
1549
|
+
body: sticker.isDup ? [] : [shape.faceVertexRanges[sticker.faceIdx]],
|
|
1550
|
+
hint: sticker.isDup ? [] : [shape.hintVertexRanges.get(sticker.faceIdx)].filter(Boolean)
|
|
1551
|
+
}));
|
|
1552
|
+
return {
|
|
1553
|
+
orbit: cubie.orbit,
|
|
1554
|
+
ord: cubie.ord,
|
|
1555
|
+
home: new Matrix42().setPosition(cubie.center),
|
|
1556
|
+
geometry,
|
|
1557
|
+
colors,
|
|
1558
|
+
facelets
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
function cubePuzzlePlan(stickerDat) {
|
|
1562
|
+
const layout = cubeLayout(stickerDat);
|
|
1563
|
+
if (!layout) {
|
|
1564
|
+
return null;
|
|
1565
|
+
}
|
|
1566
|
+
const slotWidth = 2 * layout.halfExtent / layout.layers;
|
|
1567
|
+
const shapes = /* @__PURE__ */ new Map();
|
|
1568
|
+
const pieces = layout.cubies.map((cubie) => {
|
|
1569
|
+
const shapeKey = cubie.outwardFaces.join(",");
|
|
1570
|
+
let shape = shapes.get(shapeKey);
|
|
1571
|
+
if (!shape) {
|
|
1572
|
+
shape = newCubieShape(cubie.outwardFaces, slotWidth);
|
|
1573
|
+
shapes.set(shapeKey, shape);
|
|
1574
|
+
}
|
|
1575
|
+
return newCubiePiece(cubie, shape);
|
|
1576
|
+
});
|
|
1577
|
+
return {
|
|
1578
|
+
faceStyles: cubeFaceStyles,
|
|
1579
|
+
pieces,
|
|
1580
|
+
// Oversized pieces push the puzzle past the half-extent the camera framing
|
|
1581
|
+
// assumes, so scale it back to the size `PG3D` would have drawn.
|
|
1582
|
+
scale: PG_SCALE * layout.halfExtent / (cubieBodyHalfExtent(layout.layers) * slotWidth)
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
|
|
943
1586
|
// src/cubing/twisty/views/3D/puzzles/PG3D.ts
|
|
944
1587
|
import { DoubleSide as DoubleSide2, FrontSide as FrontSide2 } from "three/src/constants.js";
|
|
945
|
-
import { BufferAttribute as
|
|
946
|
-
import { BufferGeometry as
|
|
1588
|
+
import { BufferAttribute as BufferAttribute4 } from "three/src/core/BufferAttribute.js";
|
|
1589
|
+
import { BufferGeometry as BufferGeometry4 } from "three/src/core/BufferGeometry.js";
|
|
947
1590
|
import { Object3D as Object3D2 } from "three/src/core/Object3D.js";
|
|
948
1591
|
import { MeshBasicMaterial as MeshBasicMaterial2 } from "three/src/materials/MeshBasicMaterial.js";
|
|
949
|
-
import { Color as
|
|
950
|
-
import { Vector3 as
|
|
1592
|
+
import { Color as Color3 } from "three/src/math/Color.js";
|
|
1593
|
+
import { Vector3 as Vector35 } from "three/src/math/Vector3.js";
|
|
951
1594
|
import { Group as Group2 } from "three/src/objects/Group.js";
|
|
952
1595
|
import { Mesh as Mesh2 } from "three/src/objects/Mesh.js";
|
|
953
1596
|
var foundationMaterial = new MeshBasicMaterial2({
|
|
@@ -1092,9 +1735,9 @@ var Filler = class {
|
|
|
1092
1735
|
}
|
|
1093
1736
|
}
|
|
1094
1737
|
setAttributes(geo) {
|
|
1095
|
-
geo.setAttribute("position", new
|
|
1738
|
+
geo.setAttribute("position", new BufferAttribute4(this.vertices, 3));
|
|
1096
1739
|
const sa2 = this.colors.subarray(0, 9 * this.sz);
|
|
1097
|
-
geo.setAttribute("color", new
|
|
1740
|
+
geo.setAttribute("color", new BufferAttribute4(sa2, 3, true));
|
|
1098
1741
|
}
|
|
1099
1742
|
makeGroups(geo) {
|
|
1100
1743
|
geo.clearGroups();
|
|
@@ -1129,7 +1772,7 @@ var StickerDef = class {
|
|
|
1129
1772
|
this.isDup = !!stickerDat.isDup;
|
|
1130
1773
|
this.faceNum = stickerDat.face;
|
|
1131
1774
|
this.stickerStart = filler.ipos;
|
|
1132
|
-
const sdColor = new
|
|
1775
|
+
const sdColor = new Color3(stickerDat.color).getHex();
|
|
1133
1776
|
this.origColor = sdColor;
|
|
1134
1777
|
this.origColorStickeringMask = sdColor;
|
|
1135
1778
|
if (options?.stickeringMask) {
|
|
@@ -1203,7 +1846,7 @@ var StickerDef = class {
|
|
|
1203
1846
|
if (this.origColor === 16777215) {
|
|
1204
1847
|
c = 14540253;
|
|
1205
1848
|
} else {
|
|
1206
|
-
c = new
|
|
1849
|
+
c = new Color3(this.origColor).multiplyScalar(0.5).getHex();
|
|
1207
1850
|
}
|
|
1208
1851
|
break;
|
|
1209
1852
|
}
|
|
@@ -1320,7 +1963,7 @@ var HitPlaneDef = class {
|
|
|
1320
1963
|
filler.addUncolored(coords, g);
|
|
1321
1964
|
filler.addUncolored(coords, g + 1);
|
|
1322
1965
|
}
|
|
1323
|
-
this.geo = new
|
|
1966
|
+
this.geo = new BufferGeometry4();
|
|
1324
1967
|
filler.setAttributes(this.geo);
|
|
1325
1968
|
const obj = new Mesh2(this.geo, invisMaterial);
|
|
1326
1969
|
obj.userData["quantumMove"] = stickerDat.notationMapper.notationToExternal(
|
|
@@ -1335,12 +1978,12 @@ var AxisInfo2 = class {
|
|
|
1335
1978
|
order;
|
|
1336
1979
|
constructor(axisDat) {
|
|
1337
1980
|
const vec = axisDat.coordinates;
|
|
1338
|
-
this.axis = new
|
|
1981
|
+
this.axis = new Vector35(vec[0], vec[1], vec[2]);
|
|
1339
1982
|
this.order = axisDat.order;
|
|
1340
1983
|
}
|
|
1341
1984
|
};
|
|
1342
1985
|
var DEFAULT_COLOR_FRACTION = 0.71;
|
|
1343
|
-
var
|
|
1986
|
+
var PG_SCALE2 = 0.5;
|
|
1344
1987
|
var PG3D = class extends Object3D2 {
|
|
1345
1988
|
constructor(scheduleRenderCallback, kpuzzle, stickerDat, enableFoundationOpt = false, enableHintStickersOpt = false, hintStickerHeightScale = 1, faceletScale = 1, params = {}) {
|
|
1346
1989
|
super();
|
|
@@ -1440,14 +2083,14 @@ var PG3D = class extends Object3D2 {
|
|
|
1440
2083
|
this.stickers[orbit][ori][ord].addFoundation(filler, sticker, black);
|
|
1441
2084
|
}
|
|
1442
2085
|
}
|
|
1443
|
-
const fixedGeo = new
|
|
2086
|
+
const fixedGeo = new BufferGeometry4();
|
|
1444
2087
|
filler.setAttributes(fixedGeo);
|
|
1445
2088
|
filler.makeGroups(fixedGeo);
|
|
1446
2089
|
const obj = new Mesh2(fixedGeo, this.materialArray1);
|
|
1447
|
-
obj.scale.set(
|
|
2090
|
+
obj.scale.set(PG_SCALE2, PG_SCALE2, PG_SCALE2);
|
|
1448
2091
|
this.add(obj);
|
|
1449
2092
|
const obj2 = new Mesh2(fixedGeo, this.materialArray2);
|
|
1450
|
-
obj2.scale.set(
|
|
2093
|
+
obj2.scale.set(PG_SCALE2, PG_SCALE2, PG_SCALE2);
|
|
1451
2094
|
this.add(obj2);
|
|
1452
2095
|
const hitfaces = this.stickerDat.faces;
|
|
1453
2096
|
this.movingObj = obj2;
|
|
@@ -1459,7 +2102,7 @@ var PG3D = class extends Object3D2 {
|
|
|
1459
2102
|
stickerDat.textureMapper,
|
|
1460
2103
|
this.stickerDat
|
|
1461
2104
|
);
|
|
1462
|
-
facedef.cubie.scale.set(
|
|
2105
|
+
facedef.cubie.scale.set(PG_SCALE2, PG_SCALE2, PG_SCALE2);
|
|
1463
2106
|
this.add(facedef.cubie);
|
|
1464
2107
|
this.controlTargets.push(facedef.cubie.children[0]);
|
|
1465
2108
|
}
|
|
@@ -1537,7 +2180,7 @@ var PG3D = class extends Object3D2 {
|
|
|
1537
2180
|
}
|
|
1538
2181
|
}
|
|
1539
2182
|
for (const axis of this.stickerDat.axis) {
|
|
1540
|
-
const product = point.dot(new
|
|
2183
|
+
const product = point.dot(new Vector35(...axis.coordinates));
|
|
1541
2184
|
if (product > closestMoveDotProduct) {
|
|
1542
2185
|
const modified = this.stickerDat.notationMapper.notationToExternal(
|
|
1543
2186
|
modify(axis.quantumMove)
|
|
@@ -1819,7 +2462,7 @@ var PG3D = class extends Object3D2 {
|
|
|
1819
2462
|
}
|
|
1820
2463
|
filler.uvs.copyWithin(6 * filler.sz, 0, 6 * filler.sz);
|
|
1821
2464
|
const sa1 = filler.uvs.subarray(0, 6 * filler.sz);
|
|
1822
|
-
this.fixedGeo.setAttribute("uv", new
|
|
2465
|
+
this.fixedGeo.setAttribute("uv", new BufferAttribute4(sa1, 2, true));
|
|
1823
2466
|
}
|
|
1824
2467
|
experimentalUpdateTexture(enabled, stickerTexture, hintTexture) {
|
|
1825
2468
|
if (!stickerTexture) {
|
|
@@ -1869,15 +2512,537 @@ var PG3D = class extends Object3D2 {
|
|
|
1869
2512
|
}
|
|
1870
2513
|
};
|
|
1871
2514
|
|
|
2515
|
+
// src/cubing/twisty/views/3D/puzzles/SolidPieces.ts
|
|
2516
|
+
import { BufferAttribute as BufferAttribute5 } from "three/src/core/BufferAttribute.js";
|
|
2517
|
+
import { BufferGeometry as BufferGeometry5 } from "three/src/core/BufferGeometry.js";
|
|
2518
|
+
import { Color as Color4 } from "three/src/math/Color.js";
|
|
2519
|
+
import { Matrix4 as Matrix43 } from "three/src/math/Matrix4.js";
|
|
2520
|
+
import { Vector3 as Vector37 } from "three/src/math/Vector3.js";
|
|
2521
|
+
|
|
2522
|
+
// src/cubing/twisty/views/3D/puzzles/SolidPieceGeometry.ts
|
|
2523
|
+
import { Vector3 as Vector36 } from "three/src/math/Vector3.js";
|
|
2524
|
+
var SOLID_PIECE_GROOVE = 0.018;
|
|
2525
|
+
var SOLID_PIECE_CHAMFER = 5e-3;
|
|
2526
|
+
var BOX_PLANE = -1;
|
|
2527
|
+
function boxFacet(planeIdx, normal2, size) {
|
|
2528
|
+
const reference = Math.abs(normal2.y) > 0.5 ? new Vector36(0, 0, 1) : new Vector36(0, 1, 0);
|
|
2529
|
+
const u = new Vector36().crossVectors(reference, normal2).normalize();
|
|
2530
|
+
const v = new Vector36().crossVectors(normal2, u);
|
|
2531
|
+
const center = normal2.clone().multiplyScalar(size);
|
|
2532
|
+
return {
|
|
2533
|
+
plane: planeIdx,
|
|
2534
|
+
vertices: [
|
|
2535
|
+
[-1, -1],
|
|
2536
|
+
[1, -1],
|
|
2537
|
+
[1, 1],
|
|
2538
|
+
[-1, 1]
|
|
2539
|
+
].map(
|
|
2540
|
+
([su, sv]) => center.clone().addScaledVector(u, su * size).addScaledVector(v, sv * size)
|
|
2541
|
+
)
|
|
2542
|
+
};
|
|
2543
|
+
}
|
|
2544
|
+
var BOX_NORMALS = [
|
|
2545
|
+
new Vector36(1, 0, 0),
|
|
2546
|
+
new Vector36(-1, 0, 0),
|
|
2547
|
+
new Vector36(0, 1, 0),
|
|
2548
|
+
new Vector36(0, -1, 0),
|
|
2549
|
+
new Vector36(0, 0, 1),
|
|
2550
|
+
new Vector36(0, 0, -1)
|
|
2551
|
+
];
|
|
2552
|
+
function orderInPlane(points, normal2) {
|
|
2553
|
+
const centroid = new Vector36();
|
|
2554
|
+
for (const point of points) {
|
|
2555
|
+
centroid.addScaledVector(point, 1 / points.length);
|
|
2556
|
+
}
|
|
2557
|
+
const reference = new Vector36().subVectors(points[0], centroid).normalize();
|
|
2558
|
+
const other = new Vector36().crossVectors(normal2, reference);
|
|
2559
|
+
const offset = new Vector36();
|
|
2560
|
+
return points.map((point) => {
|
|
2561
|
+
offset.subVectors(point, centroid);
|
|
2562
|
+
return {
|
|
2563
|
+
point,
|
|
2564
|
+
angle: Math.atan2(offset.dot(other), offset.dot(reference))
|
|
2565
|
+
};
|
|
2566
|
+
}).sort((a, b) => a.angle - b.angle).map(({ point }) => point);
|
|
2567
|
+
}
|
|
2568
|
+
function vertexKey(vertex, quantum) {
|
|
2569
|
+
return `${Math.round(vertex.x / quantum)},${Math.round(vertex.y / quantum)},${Math.round(vertex.z / quantum)}`;
|
|
2570
|
+
}
|
|
2571
|
+
function clipByPlane(facets, plane, planeIdx, epsilon) {
|
|
2572
|
+
const clipped = [];
|
|
2573
|
+
const capPoints = [];
|
|
2574
|
+
const capKeys = /* @__PURE__ */ new Set();
|
|
2575
|
+
const quantum = epsilon * 10;
|
|
2576
|
+
for (const facet of facets) {
|
|
2577
|
+
const kept = [];
|
|
2578
|
+
const count = facet.vertices.length;
|
|
2579
|
+
for (let i = 0; i < count; i++) {
|
|
2580
|
+
const current = facet.vertices[i];
|
|
2581
|
+
const next = facet.vertices[(i + 1) % count];
|
|
2582
|
+
const currentSide = current.dot(plane.normal) - plane.offset;
|
|
2583
|
+
const nextSide = next.dot(plane.normal) - plane.offset;
|
|
2584
|
+
if (currentSide <= epsilon) {
|
|
2585
|
+
kept.push(current);
|
|
2586
|
+
}
|
|
2587
|
+
if (currentSide > epsilon && nextSide < -epsilon || currentSide < -epsilon && nextSide > epsilon) {
|
|
2588
|
+
kept.push(
|
|
2589
|
+
current.clone().lerp(next, currentSide / (currentSide - nextSide))
|
|
2590
|
+
);
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
if (kept.length < 3) {
|
|
2594
|
+
continue;
|
|
2595
|
+
}
|
|
2596
|
+
clipped.push({ plane: facet.plane, vertices: kept });
|
|
2597
|
+
for (const vertex of kept) {
|
|
2598
|
+
if (Math.abs(vertex.dot(plane.normal) - plane.offset) > epsilon) {
|
|
2599
|
+
continue;
|
|
2600
|
+
}
|
|
2601
|
+
const key = vertexKey(vertex, quantum);
|
|
2602
|
+
if (!capKeys.has(key)) {
|
|
2603
|
+
capKeys.add(key);
|
|
2604
|
+
capPoints.push(vertex);
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
if (capPoints.length >= 3) {
|
|
2609
|
+
clipped.push({
|
|
2610
|
+
plane: planeIdx,
|
|
2611
|
+
vertices: orderInPlane(capPoints, plane.normal)
|
|
2612
|
+
});
|
|
2613
|
+
}
|
|
2614
|
+
return clipped;
|
|
2615
|
+
}
|
|
2616
|
+
function buildFacets(planes, radius, epsilon) {
|
|
2617
|
+
let facets = BOX_NORMALS.map(
|
|
2618
|
+
(normal2) => boxFacet(BOX_PLANE, normal2, radius * 4)
|
|
2619
|
+
);
|
|
2620
|
+
for (let planeIdx = 0; planeIdx < planes.length; planeIdx++) {
|
|
2621
|
+
facets = clipByPlane(facets, planes[planeIdx], planeIdx, epsilon);
|
|
2622
|
+
}
|
|
2623
|
+
if (facets.some((facet) => facet.plane === BOX_PLANE) || facets.length < 4) {
|
|
2624
|
+
return null;
|
|
2625
|
+
}
|
|
2626
|
+
return facets;
|
|
2627
|
+
}
|
|
2628
|
+
function chamferPlanes(facets, planes, chamfer, epsilon) {
|
|
2629
|
+
const quantum = epsilon * 10;
|
|
2630
|
+
const edges = /* @__PURE__ */ new Map();
|
|
2631
|
+
for (const facet of facets) {
|
|
2632
|
+
const { vertices } = facet;
|
|
2633
|
+
for (let i = 0; i < vertices.length; i++) {
|
|
2634
|
+
const edgeKey = [
|
|
2635
|
+
vertexKey(vertices[i], quantum),
|
|
2636
|
+
vertexKey(vertices[(i + 1) % vertices.length], quantum)
|
|
2637
|
+
].sort().join("|");
|
|
2638
|
+
const sharing = edges.get(edgeKey);
|
|
2639
|
+
if (sharing) {
|
|
2640
|
+
sharing.push(facet.plane);
|
|
2641
|
+
} else {
|
|
2642
|
+
edges.set(edgeKey, [facet.plane]);
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
const added = [];
|
|
2647
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2648
|
+
for (const sharing of edges.values()) {
|
|
2649
|
+
if (sharing.length !== 2) {
|
|
2650
|
+
continue;
|
|
2651
|
+
}
|
|
2652
|
+
const [a, b] = sharing;
|
|
2653
|
+
const colored = planes[a].source !== -1 ? a : b;
|
|
2654
|
+
const internal = planes[a].source !== -1 ? b : a;
|
|
2655
|
+
if (planes[colored].source === -1 || planes[internal].source !== -1) {
|
|
2656
|
+
continue;
|
|
2657
|
+
}
|
|
2658
|
+
const pairKey = `${colored}|${internal}`;
|
|
2659
|
+
if (seen.has(pairKey)) {
|
|
2660
|
+
continue;
|
|
2661
|
+
}
|
|
2662
|
+
seen.add(pairKey);
|
|
2663
|
+
const sum = new Vector36().addVectors(
|
|
2664
|
+
planes[colored].normal,
|
|
2665
|
+
planes[internal].normal
|
|
2666
|
+
);
|
|
2667
|
+
if (sum.length() < 1e-6) {
|
|
2668
|
+
continue;
|
|
2669
|
+
}
|
|
2670
|
+
added.push({
|
|
2671
|
+
normal: sum.clone().normalize(),
|
|
2672
|
+
// Where the two faces meet, measured along the bevel's own direction.
|
|
2673
|
+
offset: (planes[colored].offset + planes[internal].offset) / sum.length() - chamfer,
|
|
2674
|
+
source: planes[colored].source
|
|
2675
|
+
});
|
|
2676
|
+
}
|
|
2677
|
+
return added;
|
|
2678
|
+
}
|
|
2679
|
+
function solidPieceGeometry(planes, options) {
|
|
2680
|
+
const epsilon = options.radius * 1e-6;
|
|
2681
|
+
const working = planes.map((plane, index) => ({
|
|
2682
|
+
normal: plane.normal,
|
|
2683
|
+
offset: plane.offset - (plane.color === null ? options.groove : 0),
|
|
2684
|
+
source: plane.color === null ? -1 : index
|
|
2685
|
+
}));
|
|
2686
|
+
const plain = buildFacets(working, options.radius, epsilon);
|
|
2687
|
+
if (!plain) {
|
|
2688
|
+
return null;
|
|
2689
|
+
}
|
|
2690
|
+
const withChamfers = options.chamfer > 0 ? working.concat(chamferPlanes(plain, working, options.chamfer, epsilon)) : working;
|
|
2691
|
+
const facets = (options.chamfer > 0 ? buildFacets(withChamfers, options.radius, epsilon) : plain) ?? plain;
|
|
2692
|
+
const positions = [];
|
|
2693
|
+
const normals = [];
|
|
2694
|
+
const ranges = planes.map(() => []);
|
|
2695
|
+
const internalRanges = [];
|
|
2696
|
+
for (const facet of facets) {
|
|
2697
|
+
const plane = withChamfers[facet.plane];
|
|
2698
|
+
const start = positions.length / 3;
|
|
2699
|
+
for (let i = 1; i < facet.vertices.length - 1; i++) {
|
|
2700
|
+
for (const vertex of [
|
|
2701
|
+
facet.vertices[0],
|
|
2702
|
+
facet.vertices[i],
|
|
2703
|
+
facet.vertices[i + 1]
|
|
2704
|
+
]) {
|
|
2705
|
+
positions.push(vertex.x, vertex.y, vertex.z);
|
|
2706
|
+
normals.push(plane.normal.x, plane.normal.y, plane.normal.z);
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
const range = { start, count: positions.length / 3 - start };
|
|
2710
|
+
if (range.count === 0) {
|
|
2711
|
+
continue;
|
|
2712
|
+
}
|
|
2713
|
+
if (plane.source === -1) {
|
|
2714
|
+
internalRanges.push(range);
|
|
2715
|
+
} else {
|
|
2716
|
+
ranges[plane.source].push(range);
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
return {
|
|
2720
|
+
positions: new Float32Array(positions),
|
|
2721
|
+
normals: new Float32Array(normals),
|
|
2722
|
+
vertexCount: positions.length / 3,
|
|
2723
|
+
ranges,
|
|
2724
|
+
internalRanges
|
|
2725
|
+
};
|
|
2726
|
+
}
|
|
2727
|
+
|
|
2728
|
+
// src/cubing/twisty/views/3D/puzzles/SolidPieces.ts
|
|
2729
|
+
var PG_SCALE3 = 0.5;
|
|
2730
|
+
var EPSILON = 1e-6;
|
|
2731
|
+
function vertexAt(sticker, index) {
|
|
2732
|
+
return new Vector37(
|
|
2733
|
+
sticker.coords[3 * index],
|
|
2734
|
+
sticker.coords[3 * index + 1],
|
|
2735
|
+
sticker.coords[3 * index + 2]
|
|
2736
|
+
);
|
|
2737
|
+
}
|
|
2738
|
+
function polygonOf(sticker) {
|
|
2739
|
+
const vertices = [];
|
|
2740
|
+
for (let i = 0; i < sticker.coords.length / 3; i++) {
|
|
2741
|
+
vertices.push(vertexAt(sticker, i));
|
|
2742
|
+
}
|
|
2743
|
+
return vertices;
|
|
2744
|
+
}
|
|
2745
|
+
function outwardNormal(polygon) {
|
|
2746
|
+
const normal2 = new Vector37().crossVectors(
|
|
2747
|
+
new Vector37().subVectors(polygon[1], polygon[0]),
|
|
2748
|
+
new Vector37().subVectors(polygon[2], polygon[0])
|
|
2749
|
+
);
|
|
2750
|
+
if (normal2.length() < EPSILON) {
|
|
2751
|
+
return null;
|
|
2752
|
+
}
|
|
2753
|
+
normal2.normalize();
|
|
2754
|
+
return normal2.dot(polygon[0]) < 0 ? normal2.negate() : normal2;
|
|
2755
|
+
}
|
|
2756
|
+
function keyOf(vertex, quantum) {
|
|
2757
|
+
return `${Math.round(vertex.x / quantum)},${Math.round(vertex.y / quantum)},${Math.round(vertex.z / quantum)}`;
|
|
2758
|
+
}
|
|
2759
|
+
function faceStyles(faceNormals, faceColors) {
|
|
2760
|
+
const styles = [];
|
|
2761
|
+
const cubeFaces = [];
|
|
2762
|
+
for (let face2 = 0; face2 < faceNormals.length; face2++) {
|
|
2763
|
+
const normal2 = faceNormals[face2];
|
|
2764
|
+
const color = faceColors[face2];
|
|
2765
|
+
if (!normal2 || color === void 0) {
|
|
2766
|
+
return null;
|
|
2767
|
+
}
|
|
2768
|
+
cubeFaces.push(
|
|
2769
|
+
cubeFaceStyles.findIndex((style) => style.vector.dot(normal2) > 0.999)
|
|
2770
|
+
);
|
|
2771
|
+
}
|
|
2772
|
+
const isCube = faceNormals.length === cubeFaceStyles.length && new Set(cubeFaces).size === cubeFaceStyles.length && !cubeFaces.includes(-1) && new Set(faceColors).size === faceColors.length;
|
|
2773
|
+
for (let face2 = 0; face2 < faceNormals.length; face2++) {
|
|
2774
|
+
if (isCube) {
|
|
2775
|
+
styles.push(cubeFaceStyles[cubeFaces[face2]]);
|
|
2776
|
+
continue;
|
|
2777
|
+
}
|
|
2778
|
+
const color = new Color4(faceColors[face2]).getHex();
|
|
2779
|
+
const dimColor = new Color4(color).multiplyScalar(0.5).getHex();
|
|
2780
|
+
styles.push({
|
|
2781
|
+
color,
|
|
2782
|
+
dimColor,
|
|
2783
|
+
hintColor: color,
|
|
2784
|
+
hintDimColor: dimColor,
|
|
2785
|
+
hintOpacityScale: 1
|
|
2786
|
+
});
|
|
2787
|
+
}
|
|
2788
|
+
return styles;
|
|
2789
|
+
}
|
|
2790
|
+
function hintPolygon(polygon, normal2) {
|
|
2791
|
+
const centroid = new Vector37();
|
|
2792
|
+
for (const vertex of polygon) {
|
|
2793
|
+
centroid.addScaledVector(vertex, 1 / polygon.length);
|
|
2794
|
+
}
|
|
2795
|
+
return polygon.map(
|
|
2796
|
+
(vertex) => centroid.clone().addScaledVector(
|
|
2797
|
+
new Vector37().subVectors(vertex, centroid),
|
|
2798
|
+
HINT_FACELET_SCALE
|
|
2799
|
+
).addScaledVector(normal2, HINT_FACELET_ELEVATION)
|
|
2800
|
+
);
|
|
2801
|
+
}
|
|
2802
|
+
function solidPuzzlePlan(stickerDat) {
|
|
2803
|
+
const { stickers } = stickerDat;
|
|
2804
|
+
if (stickers.length === 0 || stickerDat.faces.length === 0) {
|
|
2805
|
+
return null;
|
|
2806
|
+
}
|
|
2807
|
+
let radius = 0;
|
|
2808
|
+
for (const sticker of stickers) {
|
|
2809
|
+
if (sticker.coords.length < 9 || sticker.coords.length % 3 !== 0) {
|
|
2810
|
+
return null;
|
|
2811
|
+
}
|
|
2812
|
+
for (let i = 0; i < sticker.coords.length; i += 3) {
|
|
2813
|
+
radius = Math.max(
|
|
2814
|
+
radius,
|
|
2815
|
+
Math.hypot(
|
|
2816
|
+
sticker.coords[i],
|
|
2817
|
+
sticker.coords[i + 1],
|
|
2818
|
+
sticker.coords[i + 2]
|
|
2819
|
+
)
|
|
2820
|
+
);
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
const quantum = radius * 1e-5;
|
|
2824
|
+
const faceNormals = new Array(stickerDat.faces.length);
|
|
2825
|
+
const faceColors = new Array(stickerDat.faces.length);
|
|
2826
|
+
const pieces = /* @__PURE__ */ new Map();
|
|
2827
|
+
const order = [];
|
|
2828
|
+
const edges = /* @__PURE__ */ new Map();
|
|
2829
|
+
let faceDistance = 0;
|
|
2830
|
+
for (const sticker of stickers) {
|
|
2831
|
+
const pieceKey = `${sticker.orbit}/${sticker.ord}`;
|
|
2832
|
+
let piece = pieces.get(pieceKey);
|
|
2833
|
+
if (!piece) {
|
|
2834
|
+
piece = {
|
|
2835
|
+
orbit: sticker.orbit,
|
|
2836
|
+
ord: sticker.ord,
|
|
2837
|
+
stickers: [],
|
|
2838
|
+
duplicates: []
|
|
2839
|
+
};
|
|
2840
|
+
pieces.set(pieceKey, piece);
|
|
2841
|
+
order.push(piece);
|
|
2842
|
+
}
|
|
2843
|
+
if (sticker.isDup) {
|
|
2844
|
+
piece.duplicates.push(sticker);
|
|
2845
|
+
continue;
|
|
2846
|
+
}
|
|
2847
|
+
const polygon = polygonOf(sticker);
|
|
2848
|
+
const normal2 = outwardNormal(polygon);
|
|
2849
|
+
if (!normal2) {
|
|
2850
|
+
return null;
|
|
2851
|
+
}
|
|
2852
|
+
for (const vertex of polygon) {
|
|
2853
|
+
if (Math.abs(vertex.dot(normal2) - polygon[0].dot(normal2)) > quantum) {
|
|
2854
|
+
return null;
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
const distance = polygon[0].dot(normal2);
|
|
2858
|
+
faceDistance = faceDistance || distance;
|
|
2859
|
+
if (faceNormals[sticker.face]) {
|
|
2860
|
+
if (faceNormals[sticker.face].dot(normal2) < 0.999) {
|
|
2861
|
+
return null;
|
|
2862
|
+
}
|
|
2863
|
+
} else {
|
|
2864
|
+
faceNormals[sticker.face] = normal2;
|
|
2865
|
+
}
|
|
2866
|
+
faceColors[sticker.face] ??= sticker.color;
|
|
2867
|
+
if (faceColors[sticker.face] !== sticker.color) {
|
|
2868
|
+
return null;
|
|
2869
|
+
}
|
|
2870
|
+
piece.stickers.push({ sticker, polygon });
|
|
2871
|
+
for (let i = 0; i < polygon.length; i++) {
|
|
2872
|
+
const edgeKey = [
|
|
2873
|
+
keyOf(polygon[i], quantum),
|
|
2874
|
+
keyOf(polygon[(i + 1) % polygon.length], quantum)
|
|
2875
|
+
].sort().join("|");
|
|
2876
|
+
const sharing = edges.get(edgeKey);
|
|
2877
|
+
if (sharing) {
|
|
2878
|
+
sharing.add(pieceKey);
|
|
2879
|
+
} else {
|
|
2880
|
+
edges.set(edgeKey, /* @__PURE__ */ new Set([pieceKey]));
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
const styles = faceStyles(faceNormals, faceColors);
|
|
2885
|
+
if (!styles) {
|
|
2886
|
+
return null;
|
|
2887
|
+
}
|
|
2888
|
+
const axes = stickerDat.axis.map(
|
|
2889
|
+
(axis) => new Vector37(...axis.coordinates).normalize()
|
|
2890
|
+
);
|
|
2891
|
+
const plans = [];
|
|
2892
|
+
for (const piece of order) {
|
|
2893
|
+
const plan = newSolidPiece(piece, edges, axes, {
|
|
2894
|
+
quantum,
|
|
2895
|
+
radius,
|
|
2896
|
+
faceDistance
|
|
2897
|
+
});
|
|
2898
|
+
if (!plan) {
|
|
2899
|
+
return null;
|
|
2900
|
+
}
|
|
2901
|
+
plans.push(plan);
|
|
2902
|
+
}
|
|
2903
|
+
return { faceStyles: styles, pieces: plans, scale: PG_SCALE3 };
|
|
2904
|
+
}
|
|
2905
|
+
function newSolidPiece(piece, edges, axes, sizes) {
|
|
2906
|
+
if (piece.stickers.length === 0) {
|
|
2907
|
+
return null;
|
|
2908
|
+
}
|
|
2909
|
+
const pieceKey = `${piece.orbit}/${piece.ord}`;
|
|
2910
|
+
const planes = [];
|
|
2911
|
+
const planeOfSticker = /* @__PURE__ */ new Map();
|
|
2912
|
+
const interior = new Vector37();
|
|
2913
|
+
for (const { polygon } of piece.stickers) {
|
|
2914
|
+
for (const vertex of polygon) {
|
|
2915
|
+
interior.addScaledVector(
|
|
2916
|
+
vertex,
|
|
2917
|
+
0.5 / polygon.length / piece.stickers.length
|
|
2918
|
+
);
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
for (const { sticker, polygon } of piece.stickers) {
|
|
2922
|
+
const normal2 = outwardNormal(polygon);
|
|
2923
|
+
planeOfSticker.set(sticker, planes.length);
|
|
2924
|
+
planes.push({
|
|
2925
|
+
normal: normal2,
|
|
2926
|
+
offset: polygon[0].dot(normal2),
|
|
2927
|
+
color: sticker.face
|
|
2928
|
+
});
|
|
2929
|
+
}
|
|
2930
|
+
for (const { polygon } of piece.stickers) {
|
|
2931
|
+
for (let i = 0; i < polygon.length; i++) {
|
|
2932
|
+
const a = polygon[i];
|
|
2933
|
+
const b = polygon[(i + 1) % polygon.length];
|
|
2934
|
+
const edgeKey = [keyOf(a, sizes.quantum), keyOf(b, sizes.quantum)].sort().join("|");
|
|
2935
|
+
const sharing = edges.get(edgeKey);
|
|
2936
|
+
if (sharing && sharing.size === 1 && sharing.has(pieceKey)) {
|
|
2937
|
+
continue;
|
|
2938
|
+
}
|
|
2939
|
+
const normal2 = new Vector37().crossVectors(a, b);
|
|
2940
|
+
if (normal2.length() < sizes.quantum) {
|
|
2941
|
+
return null;
|
|
2942
|
+
}
|
|
2943
|
+
normal2.normalize();
|
|
2944
|
+
if (normal2.dot(interior) > 0) {
|
|
2945
|
+
normal2.negate();
|
|
2946
|
+
}
|
|
2947
|
+
if (!axes.some((axis) => Math.abs(axis.dot(normal2)) > 0.9999)) {
|
|
2948
|
+
return null;
|
|
2949
|
+
}
|
|
2950
|
+
for (const other of piece.stickers) {
|
|
2951
|
+
for (const vertex of other.polygon) {
|
|
2952
|
+
if (vertex.dot(normal2) > sizes.quantum) {
|
|
2953
|
+
return null;
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
if (planes.some(
|
|
2958
|
+
(plane) => plane.color === null && plane.normal.dot(normal2) > 0.9999 && Math.abs(plane.offset) < sizes.quantum
|
|
2959
|
+
)) {
|
|
2960
|
+
continue;
|
|
2961
|
+
}
|
|
2962
|
+
planes.push({ normal: normal2, offset: 0, color: null });
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
const solid = solidPieceGeometry(planes, {
|
|
2966
|
+
groove: SOLID_PIECE_GROOVE * sizes.faceDistance,
|
|
2967
|
+
chamfer: SOLID_PIECE_CHAMFER * sizes.faceDistance,
|
|
2968
|
+
radius: sizes.radius
|
|
2969
|
+
});
|
|
2970
|
+
if (!solid) {
|
|
2971
|
+
return null;
|
|
2972
|
+
}
|
|
2973
|
+
const positions = Array.from(solid.positions);
|
|
2974
|
+
const normals = Array.from(solid.normals);
|
|
2975
|
+
const hintRanges = /* @__PURE__ */ new Map();
|
|
2976
|
+
for (const { sticker, polygon } of piece.stickers) {
|
|
2977
|
+
const normal2 = outwardNormal(polygon);
|
|
2978
|
+
const hint = hintPolygon(polygon, normal2);
|
|
2979
|
+
const start = positions.length / 3;
|
|
2980
|
+
for (let i = 1; i < hint.length - 1; i++) {
|
|
2981
|
+
for (const vertex of [hint[0], hint[i], hint[i + 1]]) {
|
|
2982
|
+
positions.push(vertex.x, vertex.y, vertex.z);
|
|
2983
|
+
normals.push(normal2.x, normal2.y, normal2.z);
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
hintRanges.set(sticker, {
|
|
2987
|
+
start,
|
|
2988
|
+
count: positions.length / 3 - start
|
|
2989
|
+
});
|
|
2990
|
+
}
|
|
2991
|
+
const vertexCount = positions.length / 3;
|
|
2992
|
+
const geometry = new BufferGeometry5();
|
|
2993
|
+
geometry.setAttribute(
|
|
2994
|
+
"position",
|
|
2995
|
+
new BufferAttribute5(new Float32Array(positions), 3)
|
|
2996
|
+
);
|
|
2997
|
+
geometry.setAttribute(
|
|
2998
|
+
"normal",
|
|
2999
|
+
new BufferAttribute5(new Float32Array(normals), 3)
|
|
3000
|
+
);
|
|
3001
|
+
const colors = new BufferAttribute5(new Uint8Array(4 * vertexCount), 4, true);
|
|
3002
|
+
geometry.setAttribute("color", colors);
|
|
3003
|
+
geometry.addGroup(0, solid.vertexCount, BODY_MATERIAL_INDEX);
|
|
3004
|
+
if (vertexCount > solid.vertexCount) {
|
|
3005
|
+
geometry.addGroup(
|
|
3006
|
+
solid.vertexCount,
|
|
3007
|
+
vertexCount - solid.vertexCount,
|
|
3008
|
+
HINT_MATERIAL_INDEX
|
|
3009
|
+
);
|
|
3010
|
+
}
|
|
3011
|
+
paintInternal(colors, solid.internalRanges);
|
|
3012
|
+
const facelets = piece.stickers.map(({ sticker }) => ({
|
|
3013
|
+
ori: sticker.ori,
|
|
3014
|
+
faceStyle: sticker.face,
|
|
3015
|
+
body: solid.ranges[planeOfSticker.get(sticker)],
|
|
3016
|
+
hint: [hintRanges.get(sticker)]
|
|
3017
|
+
}));
|
|
3018
|
+
for (const sticker of piece.duplicates) {
|
|
3019
|
+
facelets.push({
|
|
3020
|
+
ori: sticker.ori,
|
|
3021
|
+
faceStyle: sticker.face,
|
|
3022
|
+
body: [],
|
|
3023
|
+
hint: []
|
|
3024
|
+
});
|
|
3025
|
+
}
|
|
3026
|
+
return {
|
|
3027
|
+
orbit: piece.orbit,
|
|
3028
|
+
ord: piece.ord,
|
|
3029
|
+
// The geometry is already where the piece belongs.
|
|
3030
|
+
home: new Matrix43(),
|
|
3031
|
+
geometry,
|
|
3032
|
+
colors,
|
|
3033
|
+
facelets
|
|
3034
|
+
};
|
|
3035
|
+
}
|
|
3036
|
+
|
|
1872
3037
|
// src/cubing/twisty/views/3D/puzzles/Square1_3D.ts
|
|
1873
3038
|
import { DoubleSide as DoubleSide3 } from "three/src/constants.js";
|
|
1874
|
-
import { BufferAttribute as
|
|
1875
|
-
import { BufferGeometry as
|
|
3039
|
+
import { BufferAttribute as BufferAttribute6 } from "three/src/core/BufferAttribute.js";
|
|
3040
|
+
import { BufferGeometry as BufferGeometry6 } from "three/src/core/BufferGeometry.js";
|
|
1876
3041
|
import { Object3D as Object3D3 } from "three/src/core/Object3D.js";
|
|
1877
3042
|
import { MeshBasicMaterial as MeshBasicMaterial3 } from "three/src/materials/MeshBasicMaterial.js";
|
|
1878
|
-
import { Color as
|
|
3043
|
+
import { Color as Color5 } from "three/src/math/Color.js";
|
|
1879
3044
|
import { Quaternion as Quaternion2 } from "three/src/math/Quaternion.js";
|
|
1880
|
-
import { Vector3 as
|
|
3045
|
+
import { Vector3 as Vector38 } from "three/src/math/Vector3.js";
|
|
1881
3046
|
import { Group as Group3 } from "three/src/objects/Group.js";
|
|
1882
3047
|
import { Mesh as Mesh3 } from "three/src/objects/Mesh.js";
|
|
1883
3048
|
var DEGREE = TAU / 360;
|
|
@@ -1894,8 +3059,8 @@ var RING_COLORS = [2254591, 16750848, 65280, 16711680];
|
|
|
1894
3059
|
var FRAME_OFFSET_DEGREES = 180;
|
|
1895
3060
|
var BODY_COLOR = 1118481;
|
|
1896
3061
|
var MIRROR = -1;
|
|
1897
|
-
var Y_AXIS = new
|
|
1898
|
-
var SLICE_AXIS = new
|
|
3062
|
+
var Y_AXIS = new Vector38(0, 1, 0);
|
|
3063
|
+
var SLICE_AXIS = new Vector38(
|
|
1899
3064
|
MIRROR * Math.sin(15 * DEGREE),
|
|
1900
3065
|
0,
|
|
1901
3066
|
Math.cos(15 * DEGREE)
|
|
@@ -1912,7 +3077,7 @@ function slotQuaternion(slot) {
|
|
|
1912
3077
|
);
|
|
1913
3078
|
if (slot >= WEDGES_PER_LAYER) {
|
|
1914
3079
|
quaternion.multiply(
|
|
1915
|
-
new Quaternion2().setFromAxisAngle(new
|
|
3080
|
+
new Quaternion2().setFromAxisAngle(new Vector38(0, 0, 1), TAU / 2)
|
|
1916
3081
|
);
|
|
1917
3082
|
}
|
|
1918
3083
|
return quaternion;
|
|
@@ -2001,15 +3166,15 @@ function prismStickerSpecs(spec) {
|
|
|
2001
3166
|
const specs = [];
|
|
2002
3167
|
if (topColor !== null) {
|
|
2003
3168
|
specs.push({
|
|
2004
|
-
corners: polygon.map(([x, z]) => new
|
|
2005
|
-
normal: new
|
|
3169
|
+
corners: polygon.map(([x, z]) => new Vector38(x, yTop, z)),
|
|
3170
|
+
normal: new Vector38(0, 1, 0),
|
|
2006
3171
|
color: topColor
|
|
2007
3172
|
});
|
|
2008
3173
|
}
|
|
2009
3174
|
if (bottomColor !== null) {
|
|
2010
3175
|
specs.push({
|
|
2011
|
-
corners: polygon.map(([x, z]) => new
|
|
2012
|
-
normal: new
|
|
3176
|
+
corners: polygon.map(([x, z]) => new Vector38(x, yBottom, z)),
|
|
3177
|
+
normal: new Vector38(0, -1, 0),
|
|
2013
3178
|
color: bottomColor
|
|
2014
3179
|
});
|
|
2015
3180
|
}
|
|
@@ -2026,17 +3191,17 @@ function prismStickerSpecs(spec) {
|
|
|
2026
3191
|
}
|
|
2027
3192
|
const [x0, z0] = polygon[i];
|
|
2028
3193
|
const [x1, z1] = polygon[(i + 1) % polygon.length];
|
|
2029
|
-
const normal2 = new
|
|
3194
|
+
const normal2 = new Vector38(z1 - z0, 0, -(x1 - x0)).normalize();
|
|
2030
3195
|
const outward = normal2.x * ((x0 + x1) / 2 - centroidX) + normal2.z * ((z0 + z1) / 2 - centroidZ);
|
|
2031
3196
|
if (outward < 0) {
|
|
2032
3197
|
normal2.negate();
|
|
2033
3198
|
}
|
|
2034
3199
|
specs.push({
|
|
2035
3200
|
corners: [
|
|
2036
|
-
new
|
|
2037
|
-
new
|
|
2038
|
-
new
|
|
2039
|
-
new
|
|
3201
|
+
new Vector38(x0, yBottom, z0),
|
|
3202
|
+
new Vector38(x1, yBottom, z1),
|
|
3203
|
+
new Vector38(x1, yTop, z1),
|
|
3204
|
+
new Vector38(x0, yTop, z0)
|
|
2040
3205
|
],
|
|
2041
3206
|
normal: normal2,
|
|
2042
3207
|
color
|
|
@@ -2045,7 +3210,7 @@ function prismStickerSpecs(spec) {
|
|
|
2045
3210
|
return specs;
|
|
2046
3211
|
}
|
|
2047
3212
|
function stickerPositions(spec, faceletScale) {
|
|
2048
|
-
const center = new
|
|
3213
|
+
const center = new Vector38();
|
|
2049
3214
|
for (const corner of spec.corners) {
|
|
2050
3215
|
center.addScaledVector(corner, 1 / spec.corners.length);
|
|
2051
3216
|
}
|
|
@@ -2068,16 +3233,88 @@ function stickerPositions(spec, faceletScale) {
|
|
|
2068
3233
|
}
|
|
2069
3234
|
return new Float32Array(out);
|
|
2070
3235
|
}
|
|
3236
|
+
var PIECE_GROOVE_HALF_WIDTH = 0.018;
|
|
3237
|
+
var PIECE_CHAMFER = 5e-3;
|
|
3238
|
+
var PUZZLE_RADIUS = CUBE_HALF_EDGE * Math.SQRT2;
|
|
3239
|
+
function prismPlanes(spec) {
|
|
3240
|
+
const { polygon, sideColors } = spec;
|
|
3241
|
+
const planes = [
|
|
3242
|
+
{ normal: new Vector38(0, 1, 0), offset: spec.yTop, color: spec.topColor },
|
|
3243
|
+
{
|
|
3244
|
+
normal: new Vector38(0, -1, 0),
|
|
3245
|
+
offset: -spec.yBottom,
|
|
3246
|
+
color: spec.bottomColor
|
|
3247
|
+
}
|
|
3248
|
+
];
|
|
3249
|
+
let centroidX = 0;
|
|
3250
|
+
let centroidZ = 0;
|
|
3251
|
+
for (const [x, z] of polygon) {
|
|
3252
|
+
centroidX += x / polygon.length;
|
|
3253
|
+
centroidZ += z / polygon.length;
|
|
3254
|
+
}
|
|
3255
|
+
for (let i = 0; i < polygon.length; i++) {
|
|
3256
|
+
const [x0, z0] = polygon[i];
|
|
3257
|
+
const [x1, z1] = polygon[(i + 1) % polygon.length];
|
|
3258
|
+
const normal2 = new Vector38(z1 - z0, 0, -(x1 - x0)).normalize();
|
|
3259
|
+
if (normal2.x * (x0 - centroidX) + normal2.z * (z0 - centroidZ) < 0) {
|
|
3260
|
+
normal2.negate();
|
|
3261
|
+
}
|
|
3262
|
+
planes.push({
|
|
3263
|
+
normal: normal2,
|
|
3264
|
+
offset: normal2.x * x0 + normal2.z * z0,
|
|
3265
|
+
color: sideColors[i] ?? null
|
|
3266
|
+
});
|
|
3267
|
+
}
|
|
3268
|
+
return planes;
|
|
3269
|
+
}
|
|
3270
|
+
function stickerlessPieceGeometry(spec) {
|
|
3271
|
+
const planes = prismPlanes(spec);
|
|
3272
|
+
const piece = solidPieceGeometry(planes, {
|
|
3273
|
+
groove: PIECE_GROOVE_HALF_WIDTH,
|
|
3274
|
+
chamfer: PIECE_CHAMFER,
|
|
3275
|
+
radius: PUZZLE_RADIUS
|
|
3276
|
+
});
|
|
3277
|
+
if (!piece) {
|
|
3278
|
+
throw new Error("Could not build a Square-1 piece out of its planes.");
|
|
3279
|
+
}
|
|
3280
|
+
const colors = new Uint8Array(4 * piece.vertexCount);
|
|
3281
|
+
const paint = (ranges, color) => {
|
|
3282
|
+
for (const range of ranges) {
|
|
3283
|
+
for (let i = range.start; i < range.start + range.count; i++) {
|
|
3284
|
+
colors[4 * i] = color >> 16 & 255;
|
|
3285
|
+
colors[4 * i + 1] = color >> 8 & 255;
|
|
3286
|
+
colors[4 * i + 2] = color & 255;
|
|
3287
|
+
colors[4 * i + 3] = 255;
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
};
|
|
3291
|
+
paint(piece.internalRanges, bodyMaskColors.internal);
|
|
3292
|
+
for (let i = 0; i < planes.length; i++) {
|
|
3293
|
+
const color = planes[i].color;
|
|
3294
|
+
if (color !== null) {
|
|
3295
|
+
paint(piece.ranges[i], color);
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
const geometry = new BufferGeometry6();
|
|
3299
|
+
geometry.setAttribute("position", new BufferAttribute6(piece.positions, 3));
|
|
3300
|
+
geometry.setAttribute("normal", new BufferAttribute6(piece.normals, 3));
|
|
3301
|
+
geometry.setAttribute("color", new BufferAttribute6(colors, 4, true));
|
|
3302
|
+
return geometry;
|
|
3303
|
+
}
|
|
2071
3304
|
var bodyMaterial = new MeshBasicMaterial3({
|
|
2072
|
-
color: new
|
|
3305
|
+
color: new Color5(BODY_COLOR).convertLinearToSRGB(),
|
|
2073
3306
|
side: DoubleSide3
|
|
2074
3307
|
});
|
|
3308
|
+
var stickerlessMaterialCache;
|
|
3309
|
+
function stickerlessMaterial() {
|
|
3310
|
+
return stickerlessMaterialCache ??= newVertexColorBodyMaterial();
|
|
3311
|
+
}
|
|
2075
3312
|
var stickerMaterialCache = /* @__PURE__ */ new Map();
|
|
2076
3313
|
function stickerMaterial(color) {
|
|
2077
3314
|
let material = stickerMaterialCache.get(color);
|
|
2078
3315
|
if (!material) {
|
|
2079
3316
|
material = new MeshBasicMaterial3({
|
|
2080
|
-
color: new
|
|
3317
|
+
color: new Color5(color).convertLinearToSRGB(),
|
|
2081
3318
|
side: DoubleSide3
|
|
2082
3319
|
});
|
|
2083
3320
|
stickerMaterialCache.set(color, material);
|
|
@@ -2086,23 +3323,31 @@ function stickerMaterial(color) {
|
|
|
2086
3323
|
}
|
|
2087
3324
|
var Square1Piece = class extends Group3 {
|
|
2088
3325
|
body;
|
|
2089
|
-
#stickerSpecs;
|
|
3326
|
+
#stickerSpecs = [];
|
|
2090
3327
|
#stickerMeshes = [];
|
|
2091
|
-
constructor(spec, faceletScale) {
|
|
3328
|
+
constructor(spec, faceletScale, stickerless) {
|
|
2092
3329
|
super();
|
|
2093
|
-
|
|
3330
|
+
if (stickerless) {
|
|
3331
|
+
this.body = new Mesh3(
|
|
3332
|
+
stickerlessPieceGeometry(spec),
|
|
3333
|
+
stickerlessMaterial()
|
|
3334
|
+
);
|
|
3335
|
+
this.add(this.body);
|
|
3336
|
+
return;
|
|
3337
|
+
}
|
|
3338
|
+
const bodyGeometry = new BufferGeometry6();
|
|
2094
3339
|
bodyGeometry.setAttribute(
|
|
2095
3340
|
"position",
|
|
2096
|
-
new
|
|
3341
|
+
new BufferAttribute6(prismBodyPositions(spec), 3)
|
|
2097
3342
|
);
|
|
2098
3343
|
this.body = new Mesh3(bodyGeometry, bodyMaterial);
|
|
2099
3344
|
this.add(this.body);
|
|
2100
3345
|
this.#stickerSpecs = prismStickerSpecs(spec);
|
|
2101
3346
|
for (const stickerSpec of this.#stickerSpecs) {
|
|
2102
|
-
const geometry = new
|
|
3347
|
+
const geometry = new BufferGeometry6();
|
|
2103
3348
|
geometry.setAttribute(
|
|
2104
3349
|
"position",
|
|
2105
|
-
new
|
|
3350
|
+
new BufferAttribute6(stickerPositions(stickerSpec, faceletScale), 3)
|
|
2106
3351
|
);
|
|
2107
3352
|
const mesh = new Mesh3(geometry, stickerMaterial(stickerSpec.color));
|
|
2108
3353
|
this.#stickerMeshes.push(mesh);
|
|
@@ -2114,7 +3359,7 @@ var Square1Piece = class extends Group3 {
|
|
|
2114
3359
|
const geometry = this.#stickerMeshes[i].geometry;
|
|
2115
3360
|
geometry.setAttribute(
|
|
2116
3361
|
"position",
|
|
2117
|
-
new
|
|
3362
|
+
new BufferAttribute6(
|
|
2118
3363
|
stickerPositions(this.#stickerSpecs[i], faceletScale),
|
|
2119
3364
|
3
|
|
2120
3365
|
)
|
|
@@ -2122,7 +3367,9 @@ var Square1Piece = class extends Group3 {
|
|
|
2122
3367
|
}
|
|
2123
3368
|
}
|
|
2124
3369
|
setFoundationVisible(visible) {
|
|
2125
|
-
this.
|
|
3370
|
+
if (this.#stickerMeshes.length > 0) {
|
|
3371
|
+
this.body.visible = visible;
|
|
3372
|
+
}
|
|
2126
3373
|
}
|
|
2127
3374
|
};
|
|
2128
3375
|
function wedgePrismSpec(homeSlot) {
|
|
@@ -2186,8 +3433,15 @@ var Square1_3D = class extends Object3D3 {
|
|
|
2186
3433
|
if (typeof options.showFoundation === "boolean") {
|
|
2187
3434
|
this.#showFoundation = options.showFoundation;
|
|
2188
3435
|
}
|
|
3436
|
+
if (options.experimentalWedgeStyle) {
|
|
3437
|
+
this.#stickerless = options.experimentalWedgeStyle === "stickerless";
|
|
3438
|
+
}
|
|
2189
3439
|
for (let piece = 0; piece < 2 * WEDGES_PER_LAYER; piece++) {
|
|
2190
|
-
const wedge = new Square1Piece(
|
|
3440
|
+
const wedge = new Square1Piece(
|
|
3441
|
+
wedgePrismSpec(piece),
|
|
3442
|
+
this.#faceletScale,
|
|
3443
|
+
this.#stickerless
|
|
3444
|
+
);
|
|
2191
3445
|
wedge.setFoundationVisible(this.#showFoundation);
|
|
2192
3446
|
wedge.quaternion.copy(SLOT_QUATERNIONS[piece]);
|
|
2193
3447
|
this.#wedgePieces.push(wedge);
|
|
@@ -2196,7 +3450,8 @@ var Square1_3D = class extends Object3D3 {
|
|
|
2196
3450
|
for (const pair of CORNER_PAIRS) {
|
|
2197
3451
|
const corner = new Square1Piece(
|
|
2198
3452
|
cornerPrismSpec(pair),
|
|
2199
|
-
this.#faceletScale
|
|
3453
|
+
this.#faceletScale,
|
|
3454
|
+
this.#stickerless
|
|
2200
3455
|
);
|
|
2201
3456
|
corner.setFoundationVisible(this.#showFoundation);
|
|
2202
3457
|
corner.visible = false;
|
|
@@ -2206,12 +3461,16 @@ var Square1_3D = class extends Object3D3 {
|
|
|
2206
3461
|
for (let piece = 0; piece < 2; piece++) {
|
|
2207
3462
|
const equator = new Square1Piece(
|
|
2208
3463
|
equatorPrismSpec(piece),
|
|
2209
|
-
this.#faceletScale
|
|
3464
|
+
this.#faceletScale,
|
|
3465
|
+
this.#stickerless
|
|
2210
3466
|
);
|
|
2211
3467
|
equator.setFoundationVisible(this.#showFoundation);
|
|
2212
3468
|
this.#equatorPieces.push(equator);
|
|
2213
3469
|
this.add(equator);
|
|
2214
3470
|
}
|
|
3471
|
+
if (this.#stickerless) {
|
|
3472
|
+
addCubieBodyLighting(this);
|
|
3473
|
+
}
|
|
2215
3474
|
this.scale.set(PUZZLE_SCALE, PUZZLE_SCALE, PUZZLE_SCALE);
|
|
2216
3475
|
}
|
|
2217
3476
|
scheduleRenderCallback;
|
|
@@ -2220,6 +3479,7 @@ var Square1_3D = class extends Object3D3 {
|
|
|
2220
3479
|
#equatorPieces = [];
|
|
2221
3480
|
#faceletScale = DEFAULT_FACELET_SCALE;
|
|
2222
3481
|
#showFoundation = true;
|
|
3482
|
+
#stickerless = true;
|
|
2223
3483
|
#slotOfPiece = new Int8Array(2 * WEDGES_PER_LAYER);
|
|
2224
3484
|
#slotMoveMask = new Uint8Array(2 * WEDGES_PER_LAYER);
|
|
2225
3485
|
#slotRotation = new Array(2 * WEDGES_PER_LAYER).fill(
|
|
@@ -2345,13 +3605,356 @@ var Square1_3D = class extends Object3D3 {
|
|
|
2345
3605
|
}
|
|
2346
3606
|
};
|
|
2347
3607
|
|
|
3608
|
+
// src/cubing/twisty/views/3D/puzzles/Stickerless3D.ts
|
|
3609
|
+
import { BackSide as BackSide2 } from "three/src/constants.js";
|
|
3610
|
+
import { BufferAttribute as BufferAttribute7 } from "three/src/core/BufferAttribute.js";
|
|
3611
|
+
import { BufferGeometry as BufferGeometry7 } from "three/src/core/BufferGeometry.js";
|
|
3612
|
+
import { Object3D as Object3D4 } from "three/src/core/Object3D.js";
|
|
3613
|
+
import { MeshBasicMaterial as MeshBasicMaterial4 } from "three/src/materials/MeshBasicMaterial.js";
|
|
3614
|
+
import { Matrix4 as Matrix44 } from "three/src/math/Matrix4.js";
|
|
3615
|
+
import { Vector3 as Vector39 } from "three/src/math/Vector3.js";
|
|
3616
|
+
import { Mesh as Mesh4 } from "three/src/objects/Mesh.js";
|
|
3617
|
+
var invisibleMaterial2 = new MeshBasicMaterial4({ visible: false });
|
|
3618
|
+
var Stickerless3D = class extends Object3D4 {
|
|
3619
|
+
constructor(scheduleRenderCallback, kpuzzle, stickerDat, plan, options = {}) {
|
|
3620
|
+
super();
|
|
3621
|
+
this.scheduleRenderCallback = scheduleRenderCallback;
|
|
3622
|
+
this.kpuzzle = kpuzzle;
|
|
3623
|
+
this.stickerDat = stickerDat;
|
|
3624
|
+
this.plan = plan;
|
|
3625
|
+
this.#hintMaterial = new MeshBasicMaterial4({
|
|
3626
|
+
vertexColors: true,
|
|
3627
|
+
transparent: true,
|
|
3628
|
+
// Hint facelets show the faces turned away from the camera, so they are
|
|
3629
|
+
// drawn from the inside out.
|
|
3630
|
+
side: BackSide2
|
|
3631
|
+
});
|
|
3632
|
+
this.#bodyMaterial = newVertexColorBodyMaterial();
|
|
3633
|
+
this.#materials = [this.#bodyMaterial, this.#hintMaterial];
|
|
3634
|
+
this.experimentalUpdateOptions({
|
|
3635
|
+
hintFacelets: options.hintFacelets ?? "floating"
|
|
3636
|
+
});
|
|
3637
|
+
for (const axis of stickerDat.axis) {
|
|
3638
|
+
this.#axesInfo[axis.quantumMove.family] = {
|
|
3639
|
+
axis: new Vector39(...axis.coordinates),
|
|
3640
|
+
order: axis.order
|
|
3641
|
+
};
|
|
3642
|
+
}
|
|
3643
|
+
for (const piecePlan of plan.pieces) {
|
|
3644
|
+
this.#addPiece(piecePlan);
|
|
3645
|
+
}
|
|
3646
|
+
addCubieBodyLighting(this);
|
|
3647
|
+
for (const face2 of stickerDat.faces) {
|
|
3648
|
+
this.#addControlTarget(face2);
|
|
3649
|
+
}
|
|
3650
|
+
this.scale.setScalar(plan.scale);
|
|
3651
|
+
if (options.stickeringMask) {
|
|
3652
|
+
this.setStickeringMask(options.stickeringMask);
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
scheduleRenderCallback;
|
|
3656
|
+
kpuzzle;
|
|
3657
|
+
stickerDat;
|
|
3658
|
+
plan;
|
|
3659
|
+
#pieces = [];
|
|
3660
|
+
/** Indexed `[orbit][ori][ord]`, like the orbits of the `KPuzzle`. */
|
|
3661
|
+
#facelets = {};
|
|
3662
|
+
#axesInfo = {};
|
|
3663
|
+
#controlTargets = [];
|
|
3664
|
+
#materials;
|
|
3665
|
+
#bodyMaterial;
|
|
3666
|
+
#hintMaterial;
|
|
3667
|
+
#appearances = /* @__PURE__ */ new Map();
|
|
3668
|
+
/** Pieces rotated away from their home position by the last frame. */
|
|
3669
|
+
#turningPieces = [];
|
|
3670
|
+
/** Which pieces each quantum move sweeps, by the move's notation. */
|
|
3671
|
+
#turnedPieces = /* @__PURE__ */ new Map();
|
|
3672
|
+
#dirtyColors = /* @__PURE__ */ new Set();
|
|
3673
|
+
#lastPosition = null;
|
|
3674
|
+
#pendingStickeringUpdate = false;
|
|
3675
|
+
#addPiece(plan) {
|
|
3676
|
+
const mesh = new Mesh4(plan.geometry, this.#materials);
|
|
3677
|
+
mesh.matrixAutoUpdate = false;
|
|
3678
|
+
mesh.matrix.copy(plan.home);
|
|
3679
|
+
this.add(mesh);
|
|
3680
|
+
const piece = { mesh, colors: plan.colors, home: plan.home };
|
|
3681
|
+
this.#pieces.push(piece);
|
|
3682
|
+
const orbitFacelets = this.#facelets[plan.orbit] ??= [];
|
|
3683
|
+
for (const facelet of plan.facelets) {
|
|
3684
|
+
(orbitFacelets[facelet.ori] ??= [])[plan.ord] = {
|
|
3685
|
+
piece,
|
|
3686
|
+
faceStyle: facelet.faceStyle,
|
|
3687
|
+
body: facelet.body,
|
|
3688
|
+
hint: facelet.hint,
|
|
3689
|
+
appearanceKey: -1,
|
|
3690
|
+
mask: "regular",
|
|
3691
|
+
hintMask: "regular"
|
|
3692
|
+
};
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
#addControlTarget(face2) {
|
|
3696
|
+
const { coords } = face2;
|
|
3697
|
+
const triangles = coords.length / 3 - 2;
|
|
3698
|
+
const positions = new Float32Array(9 * triangles);
|
|
3699
|
+
for (let triangle = 0; triangle < triangles; triangle++) {
|
|
3700
|
+
for (const [vertex, source] of [
|
|
3701
|
+
0,
|
|
3702
|
+
triangle + 1,
|
|
3703
|
+
triangle + 2
|
|
3704
|
+
].entries()) {
|
|
3705
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
3706
|
+
positions[9 * triangle + 3 * vertex + axis] = coords[3 * source + axis];
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
const geometry = new BufferGeometry7();
|
|
3711
|
+
geometry.setAttribute("position", new BufferAttribute7(positions, 3));
|
|
3712
|
+
const mesh = new Mesh4(geometry, invisibleMaterial2);
|
|
3713
|
+
mesh.userData["quantumMove"] = this.stickerDat.notationMapper.notationToExternal(new Move(face2.name));
|
|
3714
|
+
mesh.scale.setScalar(0.99);
|
|
3715
|
+
this.add(mesh);
|
|
3716
|
+
this.#controlTargets.push(mesh);
|
|
3717
|
+
}
|
|
3718
|
+
dispose() {
|
|
3719
|
+
for (const piece of this.#pieces) {
|
|
3720
|
+
piece.mesh.geometry.dispose();
|
|
3721
|
+
}
|
|
3722
|
+
this.#bodyMaterial.dispose();
|
|
3723
|
+
this.#hintMaterial.dispose();
|
|
3724
|
+
}
|
|
3725
|
+
experimentalGetStickerTargets() {
|
|
3726
|
+
return [];
|
|
3727
|
+
}
|
|
3728
|
+
experimentalGetControlTargets() {
|
|
3729
|
+
return this.#controlTargets;
|
|
3730
|
+
}
|
|
3731
|
+
#isValidMove(move) {
|
|
3732
|
+
try {
|
|
3733
|
+
this.kpuzzle.moveToTransformation(move);
|
|
3734
|
+
return true;
|
|
3735
|
+
} catch {
|
|
3736
|
+
return false;
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
getClosestMoveToAxis(point, transformations) {
|
|
3740
|
+
let closestMove = null;
|
|
3741
|
+
let closestMoveDotProduct = 0;
|
|
3742
|
+
let modify = (m) => m;
|
|
3743
|
+
switch (transformations.depth) {
|
|
3744
|
+
case "secondSlice": {
|
|
3745
|
+
modify = (m) => m.modified({ innerLayer: 2 });
|
|
3746
|
+
break;
|
|
3747
|
+
}
|
|
3748
|
+
case "rotation": {
|
|
3749
|
+
modify = (m) => m.modified({ family: `${m.family}v` });
|
|
3750
|
+
break;
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3753
|
+
for (const axis of this.stickerDat.axis) {
|
|
3754
|
+
const product = point.dot(new Vector39(...axis.coordinates));
|
|
3755
|
+
if (product > closestMoveDotProduct) {
|
|
3756
|
+
const modified = this.stickerDat.notationMapper.notationToExternal(
|
|
3757
|
+
modify(axis.quantumMove)
|
|
3758
|
+
);
|
|
3759
|
+
if (!modified || !this.#isValidMove(modified)) {
|
|
3760
|
+
continue;
|
|
3761
|
+
}
|
|
3762
|
+
closestMoveDotProduct = product;
|
|
3763
|
+
closestMove = modified;
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
if (!closestMove) {
|
|
3767
|
+
return null;
|
|
3768
|
+
}
|
|
3769
|
+
if (transformations.invert) {
|
|
3770
|
+
closestMove = closestMove.invert();
|
|
3771
|
+
}
|
|
3772
|
+
const order = this.kpuzzle.moveToTransformation(closestMove).repetitionOrder();
|
|
3773
|
+
return { move: closestMove, order };
|
|
3774
|
+
}
|
|
3775
|
+
setStickeringMask(stickeringMask) {
|
|
3776
|
+
for (const orbitDefinition of stickeringMask.specialBehaviour === "picture" ? [] : this.kpuzzle.definition.orbits) {
|
|
3777
|
+
const { orbitName, numPieces, numOrientations } = orbitDefinition;
|
|
3778
|
+
const orbitFacelets = this.#facelets[orbitName];
|
|
3779
|
+
if (!orbitFacelets) {
|
|
3780
|
+
continue;
|
|
3781
|
+
}
|
|
3782
|
+
for (let ori = 0; ori < numOrientations; ori++) {
|
|
3783
|
+
for (let ord = 0; ord < numPieces; ord++) {
|
|
3784
|
+
const facelet = orbitFacelets[ori]?.[ord];
|
|
3785
|
+
if (!facelet) {
|
|
3786
|
+
continue;
|
|
3787
|
+
}
|
|
3788
|
+
facelet.mask = getFaceletStickeringMask(
|
|
3789
|
+
stickeringMask,
|
|
3790
|
+
orbitName,
|
|
3791
|
+
ord,
|
|
3792
|
+
ori,
|
|
3793
|
+
false
|
|
3794
|
+
);
|
|
3795
|
+
facelet.hintMask = getFaceletStickeringMask(
|
|
3796
|
+
stickeringMask,
|
|
3797
|
+
orbitName,
|
|
3798
|
+
ord,
|
|
3799
|
+
ori,
|
|
3800
|
+
true
|
|
3801
|
+
);
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
this.#pendingStickeringUpdate = true;
|
|
3806
|
+
if (this.#lastPosition) {
|
|
3807
|
+
this.onPositionChange(this.#lastPosition);
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
/** @deprecated */
|
|
3811
|
+
experimentalUpdateOptions(options) {
|
|
3812
|
+
if (options.hintFacelets !== void 0) {
|
|
3813
|
+
this.#materials[HINT_MATERIAL_INDEX] = options.hintFacelets === "none" ? invisibleMaterial2 : this.#hintMaterial;
|
|
3814
|
+
this.scheduleRenderCallback();
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
3817
|
+
/** @deprecated */
|
|
3818
|
+
experimentalUpdateTexture(enabled, stickerTexture, _hintTexture) {
|
|
3819
|
+
if (enabled && stickerTexture) {
|
|
3820
|
+
console.warn(
|
|
3821
|
+
"Sprites are not supported for stickerless pieces. For now, set the sprite before creating the TwistyPlayer."
|
|
3822
|
+
);
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
onPositionChange(position) {
|
|
3826
|
+
const { pattern } = position;
|
|
3827
|
+
if (this.#pendingStickeringUpdate || !this.#lastPosition || !this.#lastPosition.pattern.isIdentical(pattern)) {
|
|
3828
|
+
this.#updateColors(position);
|
|
3829
|
+
this.#lastPosition = position;
|
|
3830
|
+
this.#pendingStickeringUpdate = false;
|
|
3831
|
+
}
|
|
3832
|
+
for (const piece of this.#turningPieces) {
|
|
3833
|
+
piece.mesh.matrix.copy(piece.home);
|
|
3834
|
+
}
|
|
3835
|
+
this.#turningPieces.length = 0;
|
|
3836
|
+
const turnMatrix = new Matrix44();
|
|
3837
|
+
for (const moveProgress of position.movesInProgress) {
|
|
3838
|
+
const move = moveProgress.move;
|
|
3839
|
+
const unswizzled = this.stickerDat.unswizzle(move);
|
|
3840
|
+
if (!unswizzled) {
|
|
3841
|
+
continue;
|
|
3842
|
+
}
|
|
3843
|
+
const axisInfo = this.#axesInfo[unswizzled.family];
|
|
3844
|
+
if (!axisInfo) {
|
|
3845
|
+
continue;
|
|
3846
|
+
}
|
|
3847
|
+
turnMatrix.makeRotationAxis(
|
|
3848
|
+
axisInfo.axis,
|
|
3849
|
+
-smootherStep(moveProgress.fraction) * moveProgress.direction * unswizzled.amount * TAU / axisInfo.order
|
|
3850
|
+
);
|
|
3851
|
+
for (const piece of this.#piecesTurnedBy(move)) {
|
|
3852
|
+
piece.mesh.matrix.premultiply(turnMatrix);
|
|
3853
|
+
this.#turningPieces.push(piece);
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
this.scheduleRenderCallback();
|
|
3857
|
+
}
|
|
3858
|
+
/**
|
|
3859
|
+
* The pieces a single quantum of `move` sweeps: the ones whose slot does not
|
|
3860
|
+
* map to itself.
|
|
3861
|
+
*/
|
|
3862
|
+
#piecesTurnedBy(move) {
|
|
3863
|
+
const quantum = move.modified({ amount: 1 });
|
|
3864
|
+
const key = quantum.toString();
|
|
3865
|
+
const cached = this.#turnedPieces.get(key);
|
|
3866
|
+
if (cached) {
|
|
3867
|
+
return cached;
|
|
3868
|
+
}
|
|
3869
|
+
const pieces = [];
|
|
3870
|
+
const { transformationData } = this.#quantumTransformation(quantum);
|
|
3871
|
+
for (const orbitName in this.#facelets) {
|
|
3872
|
+
const orbitTransformation = transformationData[orbitName];
|
|
3873
|
+
if (!orbitTransformation) {
|
|
3874
|
+
continue;
|
|
3875
|
+
}
|
|
3876
|
+
const { permutation, orientationDelta } = orbitTransformation;
|
|
3877
|
+
const slots = this.#facelets[orbitName][0];
|
|
3878
|
+
for (let ord = 0; ord < permutation.length; ord++) {
|
|
3879
|
+
if (permutation[ord] !== ord || orientationDelta[ord] !== 0) {
|
|
3880
|
+
const slot = slots[ord];
|
|
3881
|
+
if (slot) {
|
|
3882
|
+
pieces.push(slot.piece);
|
|
3883
|
+
}
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
this.#turnedPieces.set(key, pieces);
|
|
3888
|
+
return pieces;
|
|
3889
|
+
}
|
|
3890
|
+
#quantumTransformation(quantum) {
|
|
3891
|
+
try {
|
|
3892
|
+
return this.kpuzzle.moveToTransformation(quantum);
|
|
3893
|
+
} catch (e) {
|
|
3894
|
+
const internal = this.stickerDat.notationMapper.notationToInternal(quantum);
|
|
3895
|
+
const external = internal && this.stickerDat.notationMapper.notationToExternal(
|
|
3896
|
+
internal.modified({ amount: 1 })
|
|
3897
|
+
);
|
|
3898
|
+
if (!external) {
|
|
3899
|
+
throw e;
|
|
3900
|
+
}
|
|
3901
|
+
return this.kpuzzle.moveToTransformation(external);
|
|
3902
|
+
}
|
|
3903
|
+
}
|
|
3904
|
+
/** Repaints every slot with the colors of whatever piece is now in it. */
|
|
3905
|
+
#updateColors(position) {
|
|
3906
|
+
for (const orbitName in this.#facelets) {
|
|
3907
|
+
const orbitFacelets = this.#facelets[orbitName];
|
|
3908
|
+
const orbitPattern = position.pattern.patternData[orbitName];
|
|
3909
|
+
const numOrientations = orbitFacelets.length;
|
|
3910
|
+
for (let ori = 0; ori < numOrientations; ori++) {
|
|
3911
|
+
const slots = orbitFacelets[ori];
|
|
3912
|
+
for (let ord = 0; ord < slots.length; ord++) {
|
|
3913
|
+
const slot = slots[ord];
|
|
3914
|
+
if (!slot) {
|
|
3915
|
+
continue;
|
|
3916
|
+
}
|
|
3917
|
+
const sourceOri = numOrientations === 1 ? 0 : (ori + numOrientations - orbitPattern.orientation[ord]) % numOrientations;
|
|
3918
|
+
const source = orbitFacelets[sourceOri][orbitPattern.pieces[ord]];
|
|
3919
|
+
const key = faceletAppearanceKey(
|
|
3920
|
+
source.faceStyle,
|
|
3921
|
+
source.mask,
|
|
3922
|
+
source.hintMask
|
|
3923
|
+
);
|
|
3924
|
+
if (key === slot.appearanceKey) {
|
|
3925
|
+
continue;
|
|
3926
|
+
}
|
|
3927
|
+
slot.appearanceKey = key;
|
|
3928
|
+
if (slot.body.length === 0 && slot.hint.length === 0) {
|
|
3929
|
+
continue;
|
|
3930
|
+
}
|
|
3931
|
+
const appearance = faceletAppearance(
|
|
3932
|
+
this.#appearances,
|
|
3933
|
+
this.plan.faceStyles,
|
|
3934
|
+
source.faceStyle,
|
|
3935
|
+
source.mask,
|
|
3936
|
+
source.hintMask
|
|
3937
|
+
);
|
|
3938
|
+
writeColor(slot.piece.colors, slot.body, appearance);
|
|
3939
|
+
writeColor(slot.piece.colors, slot.hint, appearance, 4);
|
|
3940
|
+
this.#dirtyColors.add(slot.piece.colors);
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
for (const colors of this.#dirtyColors) {
|
|
3945
|
+
colors.needsUpdate = true;
|
|
3946
|
+
}
|
|
3947
|
+
this.#dirtyColors.clear();
|
|
3948
|
+
}
|
|
3949
|
+
};
|
|
3950
|
+
|
|
2348
3951
|
// src/cubing/twisty/heavy-code-imports/dynamic-entries/twisty-dynamic-3d.ts
|
|
2349
3952
|
import { PerspectiveCamera } from "three/src/cameras/PerspectiveCamera.js";
|
|
2350
3953
|
import { Raycaster } from "three/src/core/Raycaster.js";
|
|
2351
3954
|
import { TextureLoader as TextureLoader2 } from "three/src/loaders/TextureLoader.js";
|
|
2352
3955
|
import { Spherical } from "three/src/math/Spherical.js";
|
|
2353
3956
|
import { Vector2 as Vector22 } from "three/src/math/Vector2.js";
|
|
2354
|
-
import { Vector3 as
|
|
3957
|
+
import { Vector3 as Vector310 } from "three/src/math/Vector3.js";
|
|
2355
3958
|
import { WebGLRenderer } from "three/src/renderers/WebGLRenderer.js";
|
|
2356
3959
|
import { Scene } from "three/src/scenes/Scene.js";
|
|
2357
3960
|
|
|
@@ -2393,11 +3996,19 @@ async function square1_3DShim(renderCallback, puzzleLoader, faceletScale) {
|
|
|
2393
3996
|
faceletScale
|
|
2394
3997
|
});
|
|
2395
3998
|
}
|
|
2396
|
-
async function pg3dShim(renderCallback, puzzleLoader, hintFacelets, faceletScale, darkIgnoredOrbits) {
|
|
3999
|
+
async function pg3dShim(renderCallback, puzzleLoader, hintFacelets, faceletScale, darkIgnoredOrbits, pictureCube = false) {
|
|
4000
|
+
const kpuzzle = await puzzleLoader.kpuzzle();
|
|
4001
|
+
const stickerDat = (await puzzleLoader.pg()).get3d({ darkIgnoredOrbits });
|
|
4002
|
+
const plan = darkIgnoredOrbits || pictureCube ? null : cubePuzzlePlan(stickerDat) ?? solidPuzzlePlan(stickerDat);
|
|
4003
|
+
if (plan) {
|
|
4004
|
+
return new Stickerless3D(renderCallback, kpuzzle, stickerDat, plan, {
|
|
4005
|
+
hintFacelets
|
|
4006
|
+
});
|
|
4007
|
+
}
|
|
2397
4008
|
return new PG3D(
|
|
2398
4009
|
renderCallback,
|
|
2399
|
-
|
|
2400
|
-
|
|
4010
|
+
kpuzzle,
|
|
4011
|
+
stickerDat,
|
|
2401
4012
|
true,
|
|
2402
4013
|
hintFacelets === "floating",
|
|
2403
4014
|
void 0,
|
|
@@ -2408,17 +4019,18 @@ export {
|
|
|
2408
4019
|
Cube3D,
|
|
2409
4020
|
PG3D,
|
|
2410
4021
|
Square1_3D,
|
|
4022
|
+
Stickerless3D,
|
|
2411
4023
|
PerspectiveCamera as ThreePerspectiveCamera,
|
|
2412
4024
|
Raycaster as ThreeRaycaster,
|
|
2413
4025
|
Scene as ThreeScene,
|
|
2414
4026
|
Spherical as ThreeSpherical,
|
|
2415
4027
|
TextureLoader2 as ThreeTextureLoader,
|
|
2416
4028
|
Vector22 as ThreeVector2,
|
|
2417
|
-
|
|
4029
|
+
Vector310 as ThreeVector3,
|
|
2418
4030
|
WebGLRenderer as ThreeWebGLRenderer,
|
|
2419
4031
|
Twisty3DScene,
|
|
2420
4032
|
cube3DShim,
|
|
2421
4033
|
pg3dShim,
|
|
2422
4034
|
square1_3DShim
|
|
2423
4035
|
};
|
|
2424
|
-
//# sourceMappingURL=twisty-dynamic-3d-
|
|
4036
|
+
//# sourceMappingURL=twisty-dynamic-3d-SLKSUJTE.js.map
|