@rednaxela101/cubing 0.63.6 → 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.
Files changed (45) hide show
  1. package/README.md +43 -57
  2. package/dist/bin/chunks/{chunk-RHJ7ANWU.js → chunk-EWR23OA4.js} +2 -2
  3. package/dist/bin/order.js +1 -1
  4. package/dist/bin/puzzle-geometry-bin.js +1 -1
  5. package/dist/bin/scramble.js +1 -1
  6. package/dist/bin/svg.js +1 -1
  7. package/dist/lib/cubing/bluetooth/index.js +3 -3
  8. package/dist/lib/cubing/chunks/{chunk-LSVH6VHD.js → chunk-3ODTF4HE.js} +2 -2
  9. package/dist/lib/cubing/chunks/{chunk-SO7SGUBW.js → chunk-B2YKAP2I.js} +3 -3
  10. package/dist/lib/cubing/chunks/{chunk-76YTBNKC.js → chunk-GDABMAWL.js} +3 -3
  11. package/dist/lib/cubing/chunks/{chunk-VBJPGWEK.js → chunk-GHYWEEAX.js} +11 -1
  12. package/dist/lib/cubing/chunks/chunk-GHYWEEAX.js.map +7 -0
  13. package/dist/lib/cubing/chunks/{chunk-4K2UWZ73.js → chunk-OQK27CVX.js} +2 -2
  14. package/dist/lib/cubing/chunks/{chunk-SCBMPWH6.js → chunk-S3MH37QE.js} +2 -2
  15. package/dist/lib/cubing/chunks/{chunk-YQSSBUO3.js → chunk-ZQLJIHAG.js} +2 -2
  16. package/dist/lib/cubing/chunks/{inside-77AO4ZS2.js → inside-GINHTLIR.js} +9 -9
  17. package/dist/lib/cubing/chunks/{search-dynamic-sgs-2x2x2-HX32MEXL.js → search-dynamic-sgs-2x2x2-VUBKGY5W.js} +3 -3
  18. package/dist/lib/cubing/chunks/{search-dynamic-sgs-pyraminx-PCQDPIYE.js → search-dynamic-sgs-pyraminx-NUYNXXKF.js} +3 -3
  19. package/dist/lib/cubing/chunks/{search-dynamic-sgs-unofficial-TI3CHZMS.js → search-dynamic-sgs-unofficial-C74IYCYM.js} +3 -3
  20. package/dist/lib/cubing/chunks/{search-dynamic-solve-4x4x4-O3ATNPFN.js → search-dynamic-solve-4x4x4-DCLOGJPG.js} +5 -5
  21. package/dist/lib/cubing/chunks/search-worker-entry.js +1 -1
  22. package/dist/lib/cubing/chunks/{twisty-dynamic-3d-VXNE3XL4.js → twisty-dynamic-3d-SLKSUJTE.js} +1806 -194
  23. package/dist/lib/cubing/chunks/twisty-dynamic-3d-SLKSUJTE.js.map +7 -0
  24. package/dist/lib/cubing/notation/index.js +2 -2
  25. package/dist/lib/cubing/protocol/index.js +2 -2
  26. package/dist/lib/cubing/puzzles/index.js +2 -2
  27. package/dist/lib/cubing/scramble/index.js +4 -4
  28. package/dist/lib/cubing/search/index.js +4 -4
  29. package/dist/lib/cubing/twisty/index.js +6 -5
  30. package/dist/lib/cubing/twisty/index.js.map +2 -2
  31. package/package.json +1 -1
  32. package/dist/lib/cubing/chunks/chunk-VBJPGWEK.js.map +0 -7
  33. package/dist/lib/cubing/chunks/twisty-dynamic-3d-VXNE3XL4.js.map +0 -7
  34. /package/dist/bin/chunks/{chunk-RHJ7ANWU.js.map → chunk-EWR23OA4.js.map} +0 -0
  35. /package/dist/lib/cubing/chunks/{chunk-LSVH6VHD.js.map → chunk-3ODTF4HE.js.map} +0 -0
  36. /package/dist/lib/cubing/chunks/{chunk-SO7SGUBW.js.map → chunk-B2YKAP2I.js.map} +0 -0
  37. /package/dist/lib/cubing/chunks/{chunk-76YTBNKC.js.map → chunk-GDABMAWL.js.map} +0 -0
  38. /package/dist/lib/cubing/chunks/{chunk-4K2UWZ73.js.map → chunk-OQK27CVX.js.map} +0 -0
  39. /package/dist/lib/cubing/chunks/{chunk-SCBMPWH6.js.map → chunk-S3MH37QE.js.map} +0 -0
  40. /package/dist/lib/cubing/chunks/{chunk-YQSSBUO3.js.map → chunk-ZQLJIHAG.js.map} +0 -0
  41. /package/dist/lib/cubing/chunks/{inside-77AO4ZS2.js.map → inside-GINHTLIR.js.map} +0 -0
  42. /package/dist/lib/cubing/chunks/{search-dynamic-sgs-2x2x2-HX32MEXL.js.map → search-dynamic-sgs-2x2x2-VUBKGY5W.js.map} +0 -0
  43. /package/dist/lib/cubing/chunks/{search-dynamic-sgs-pyraminx-PCQDPIYE.js.map → search-dynamic-sgs-pyraminx-NUYNXXKF.js.map} +0 -0
  44. /package/dist/lib/cubing/chunks/{search-dynamic-sgs-unofficial-TI3CHZMS.js.map → search-dynamic-sgs-unofficial-C74IYCYM.js.map} +0 -0
  45. /package/dist/lib/cubing/chunks/{search-dynamic-solve-4x4x4-O3ATNPFN.js.map → search-dynamic-solve-4x4x4-DCLOGJPG.js.map} +0 -0
@@ -3,12 +3,12 @@ import {
3
3
  bulk3DCode,
4
4
  haveStartedSharingRenderers,
5
5
  hintFaceletStyles
6
- } from "./chunk-SO7SGUBW.js";
7
- import "./chunk-LSVH6VHD.js";
6
+ } from "./chunk-B2YKAP2I.js";
7
+ import "./chunk-3ODTF4HE.js";
8
8
  import {
9
9
  cube3x3x3,
10
10
  getFaceletStickeringMask
11
- } from "./chunk-VBJPGWEK.js";
11
+ } from "./chunk-GHYWEEAX.js";
12
12
  import "./chunk-52ZXJEUO.js";
13
13
  import {
14
14
  Move
@@ -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 Color(6710886).convertLinearToSRGB()
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: 4513228
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: 16776618
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: 15911883
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
- constructor(vector, fromZ, color, dimColor, hintOpacityScale, options) {
82
- this.vector = vector;
83
- this.fromZ = fromZ;
84
- this.color = color;
85
- this.dimColor = dimColor;
86
- this.hintOpacityScale = hintOpacityScale;
87
- const colorLinearSRGB = new Color(color).convertLinearToSRGB();
88
- const dimColorLinearSRGB = new Color(dimColor).convertLinearToSRGB();
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: new MeshBasicMaterial({
108
- color: new Color(options?.hintColor ?? color).convertLinearToSRGB(),
109
- side: BackSide,
110
- transparent: true,
389
+ regular: newHintMaterial({
390
+ color: style.hintColor,
111
391
  opacity: 0.5 * hintOpacityScale
112
392
  }),
113
- dim: new MeshBasicMaterial({
114
- color: new 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 Vector3(0, 0, 0),
279
- U: new Vector3(0, -1, 0),
280
- L: new Vector3(1, 0, 0),
281
- F: new Vector3(0, 0, -1),
282
- R: new Vector3(-1, 0, 0),
283
- B: new Vector3(0, 0, 1),
284
- D: new Vector3(0, 1, 0)
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 Vector3(0, 1, 1),
288
- CORNERS: new Vector3(1, 1, 1),
289
- CENTERS: new Vector3(0, 1, 0)
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 BufferGeometry();
701
+ const r2 = new BufferGeometry2();
459
702
  const half = 0.5;
460
703
  r2.setAttribute(
461
704
  "position",
462
- new BufferAttribute(
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 BufferAttribute(
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.scale.set(CUBE_SCALE, CUBE_SCALE, CUBE_SCALE);
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.facelet.material = invisibleMaterial;
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.facelet.material = axesInfo[faceletInfo.faceIdx].stickerMaterial[stickeringMask2];
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
- if (this.options.showFoundation) {
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 sticker = this.createSticker(
779
- axesInfo[cubieStickerOrder[i]],
780
- axesInfo[piece.stickerFaces[i]],
781
- false
782
- );
783
- const faceletInfo = {
784
- faceIdx: piece.stickerFaces[i],
785
- facelet: sticker
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
- cubie.add(sticker);
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[piece.stickerFaces[i]],
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 BufferAttribute(
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.facelet.scale.setX(scale);
917
- faceletInfo.facelet.scale.setY(scale);
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 BufferAttribute2 } from "three/src/core/BufferAttribute.js";
946
- import { BufferGeometry as BufferGeometry2 } from "three/src/core/BufferGeometry.js";
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 Color2 } from "three/src/math/Color.js";
950
- import { Vector3 as Vector32 } from "three/src/math/Vector3.js";
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 BufferAttribute2(this.vertices, 3));
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 BufferAttribute2(sa2, 3, true));
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 Color2(stickerDat.color).getHex();
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 Color2(this.origColor).multiplyScalar(0.5).getHex();
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 BufferGeometry2();
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 Vector32(vec[0], vec[1], vec[2]);
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 PG_SCALE = 0.5;
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 BufferGeometry2();
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(PG_SCALE, PG_SCALE, PG_SCALE);
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(PG_SCALE, PG_SCALE, PG_SCALE);
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(PG_SCALE, PG_SCALE, PG_SCALE);
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 Vector32(...axis.coordinates));
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 BufferAttribute2(sa1, 2, true));
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 BufferAttribute3 } from "three/src/core/BufferAttribute.js";
1875
- import { BufferGeometry as BufferGeometry3 } from "three/src/core/BufferGeometry.js";
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 Color3 } from "three/src/math/Color.js";
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 Vector33 } from "three/src/math/Vector3.js";
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 Vector33(0, 1, 0);
1898
- var SLICE_AXIS = new Vector33(
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 Vector33(0, 0, 1), TAU / 2)
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 Vector33(x, yTop, z)),
2005
- normal: new Vector33(0, 1, 0),
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 Vector33(x, yBottom, z)),
2012
- normal: new Vector33(0, -1, 0),
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 Vector33(z1 - z0, 0, -(x1 - x0)).normalize();
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 Vector33(x0, yBottom, z0),
2037
- new Vector33(x1, yBottom, z1),
2038
- new Vector33(x1, yTop, z1),
2039
- new Vector33(x0, yTop, z0)
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 Vector33();
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 Color3(BODY_COLOR).convertLinearToSRGB(),
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 Color3(color).convertLinearToSRGB(),
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
- const bodyGeometry = new BufferGeometry3();
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 BufferAttribute3(prismBodyPositions(spec), 3)
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 BufferGeometry3();
3347
+ const geometry = new BufferGeometry6();
2103
3348
  geometry.setAttribute(
2104
3349
  "position",
2105
- new BufferAttribute3(stickerPositions(stickerSpec, faceletScale), 3)
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 BufferAttribute3(
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.body.visible = visible;
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(wedgePrismSpec(piece), this.#faceletScale);
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 Vector34 } from "three/src/math/Vector3.js";
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
- await puzzleLoader.kpuzzle(),
2400
- (await puzzleLoader.pg()).get3d({ darkIgnoredOrbits }),
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
- Vector34 as ThreeVector3,
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-VXNE3XL4.js.map
4036
+ //# sourceMappingURL=twisty-dynamic-3d-SLKSUJTE.js.map