@rednaxela101/cubing 0.63.7 → 0.63.9

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