paperlab 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -18
- package/dist/{chunk-4ZU5DZEF.js → chunk-HRXQTJFS.js} +526 -225
- package/dist/chunk-HRXQTJFS.js.map +1 -0
- package/dist/index.cjs +590 -274
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -65
- package/dist/index.d.ts +36 -65
- package/dist/index.js +26 -3
- package/dist/index.js.map +1 -1
- package/dist/slots-CIo7FnlY.d.cts +13357 -0
- package/dist/slots-CIo7FnlY.d.ts +13357 -0
- package/dist/stage.cjs +2448 -2159
- package/dist/stage.cjs.map +1 -1
- package/dist/stage.d.cts +52 -28
- package/dist/stage.d.ts +52 -28
- package/dist/stage.js +41 -15
- package/dist/stage.js.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-4ZU5DZEF.js.map +0 -1
- package/dist/slots-BYoLxVZ4.d.cts +0 -6008
- package/dist/slots-BYoLxVZ4.d.ts +0 -6008
package/dist/index.cjs
CHANGED
|
@@ -36,17 +36,21 @@ __export(index_exports, {
|
|
|
36
36
|
LightRig: () => LightRig,
|
|
37
37
|
PARITY_EPSILON: () => PARITY_EPSILON,
|
|
38
38
|
Paper: () => Paper,
|
|
39
|
+
PaperBackdrop: () => PaperBackdrop,
|
|
39
40
|
PaperField: () => PaperField,
|
|
40
41
|
PaperFieldMesh: () => PaperFieldMesh,
|
|
41
42
|
PaperLighting: () => PaperLighting,
|
|
42
43
|
PaperMesh: () => PaperMesh,
|
|
43
44
|
SAG_TOL: () => SAG_TOL,
|
|
45
|
+
backdropSchema: () => backdropSchema,
|
|
44
46
|
behaviorConfigSchema: () => behaviorConfigSchema,
|
|
45
47
|
buildAgentPayload: () => buildAgentPayload,
|
|
46
48
|
buildFieldAgentPayload: () => buildFieldAgentPayload,
|
|
47
49
|
buildFieldComponentSource: () => buildFieldComponentSource,
|
|
48
50
|
buildJsxSnippet: () => buildJsxSnippet,
|
|
49
51
|
clothConfigSchema: () => clothConfigSchema,
|
|
52
|
+
contentNames: () => contentNames,
|
|
53
|
+
contentSchemaFor: () => contentSchemaFor,
|
|
50
54
|
coreStateNames: () => coreStateNames,
|
|
51
55
|
describeConfig: () => describeConfig,
|
|
52
56
|
describeFieldConfig: () => describeFieldConfig,
|
|
@@ -83,6 +87,7 @@ __export(index_exports, {
|
|
|
83
87
|
resolveLighting: () => resolveLighting,
|
|
84
88
|
resolveStateConfig: () => resolveStateConfig,
|
|
85
89
|
runParityHarness: () => runParityHarness,
|
|
90
|
+
sceneSchema: () => sceneSchema,
|
|
86
91
|
segmentsForArc: () => segmentsForArc,
|
|
87
92
|
segmentsForSine: () => segmentsForSine,
|
|
88
93
|
serializePreset: () => serializePreset,
|
|
@@ -96,13 +101,14 @@ __export(index_exports, {
|
|
|
96
101
|
unregisterPreset: () => unregisterPreset,
|
|
97
102
|
usePaperStates: () => usePaperStates,
|
|
98
103
|
usePrefersReducedMotion: () => usePrefersReducedMotion,
|
|
104
|
+
washSchema: () => washSchema,
|
|
99
105
|
wrapLines: () => wrapLines
|
|
100
106
|
});
|
|
101
107
|
module.exports = __toCommonJS(index_exports);
|
|
102
108
|
|
|
103
109
|
// src/Paper.tsx
|
|
104
|
-
var
|
|
105
|
-
var
|
|
110
|
+
var import_fiber5 = require("@react-three/fiber");
|
|
111
|
+
var import_react11 = require("react");
|
|
106
112
|
|
|
107
113
|
// src/PaperMesh.tsx
|
|
108
114
|
var THREE7 = __toESM(require("three"), 1);
|
|
@@ -1023,6 +1029,31 @@ var stockNames = [
|
|
|
1023
1029
|
"sticker"
|
|
1024
1030
|
];
|
|
1025
1031
|
var stockSchema = import_zod14.z.enum(stockNames);
|
|
1032
|
+
var washSchema = import_zod14.z.object({
|
|
1033
|
+
/** The first pigment. */
|
|
1034
|
+
color: import_zod14.z.string().default("#4a5b8c").describe("color"),
|
|
1035
|
+
/** The second. Blooms alternate, and overlaps multiply into a third. */
|
|
1036
|
+
secondary: import_zod14.z.string().default("#b06a6a").describe("color"),
|
|
1037
|
+
/** How many pools of colour. */
|
|
1038
|
+
blooms: import_zod14.z.number().int().min(1).max(24).default(7),
|
|
1039
|
+
/** How far a pool runs before it dries — its size against the sheet. */
|
|
1040
|
+
spread: import_zod14.z.number().min(0.1).max(1).default(0.7),
|
|
1041
|
+
/** Softness of the wet edge. 0 is a hard cut, 1 is a pool still moving. */
|
|
1042
|
+
bleed: import_zod14.z.number().min(0).max(1).default(0.5),
|
|
1043
|
+
/** How much pigment is in the water. */
|
|
1044
|
+
intensity: import_zod14.z.number().min(0).max(1).default(0.55),
|
|
1045
|
+
/**
|
|
1046
|
+
* Edge darkening — the ring of pigment left where a pool dried.
|
|
1047
|
+
*
|
|
1048
|
+
* The signature of the medium, and the one thing a plain gradient cannot
|
|
1049
|
+
* fake. Without it a wash reads as an airbrush.
|
|
1050
|
+
*/
|
|
1051
|
+
edge: import_zod14.z.number().min(0).max(1).default(0.6),
|
|
1052
|
+
/** Pigment settling into the tooth of the paper. */
|
|
1053
|
+
granulation: import_zod14.z.number().min(0).max(1).default(0.35),
|
|
1054
|
+
/** Fixed so a preset paints the same wash every time. */
|
|
1055
|
+
seed: import_zod14.z.number().int().min(0).max(99).default(0)
|
|
1056
|
+
});
|
|
1026
1057
|
var blankContentBase = import_zod14.z.object({
|
|
1027
1058
|
type: import_zod14.z.literal("blank")
|
|
1028
1059
|
});
|
|
@@ -1047,7 +1078,7 @@ var textContentBase = import_zod14.z.object({
|
|
|
1047
1078
|
/** px at texture resolution (long edge = 1024 logical px before DPR). */
|
|
1048
1079
|
size: import_zod14.z.number().min(8).max(256).default(44),
|
|
1049
1080
|
weight: import_zod14.z.number().min(100).max(900).default(400),
|
|
1050
|
-
color: import_zod14.z.string().default("#2b2620"),
|
|
1081
|
+
color: import_zod14.z.string().default("#2b2620").describe("color"),
|
|
1051
1082
|
align: import_zod14.z.enum(["left", "center", "right"]).default("left"),
|
|
1052
1083
|
/** Fraction of the short edge. */
|
|
1053
1084
|
padding: import_zod14.z.number().min(0).max(0.4).default(0.09),
|
|
@@ -1097,7 +1128,7 @@ var cardContentBase = import_zod14.z.object({
|
|
|
1097
1128
|
* was cropped rather than as a card that was set.
|
|
1098
1129
|
*/
|
|
1099
1130
|
size: import_zod14.z.number().min(8).max(256).default(58),
|
|
1100
|
-
color: import_zod14.z.string().default("#2b2620"),
|
|
1131
|
+
color: import_zod14.z.string().default("#2b2620").describe("color"),
|
|
1101
1132
|
align: import_zod14.z.enum(["left", "center"]).default("left"),
|
|
1102
1133
|
padding: import_zod14.z.number().min(0).max(0.4).default(0.1)
|
|
1103
1134
|
});
|
|
@@ -1116,14 +1147,15 @@ var receiptContentBase = import_zod14.z.object({
|
|
|
1116
1147
|
timestamp: import_zod14.z.string().optional(),
|
|
1117
1148
|
footer: import_zod14.z.string().default("KEEP FOR YOUR RECORDS")
|
|
1118
1149
|
});
|
|
1150
|
+
var withWash = { wash: washSchema.optional() };
|
|
1119
1151
|
var backContentSchema = import_zod14.z.discriminatedUnion("type", [
|
|
1120
|
-
blankContentBase,
|
|
1121
|
-
imageContentBase,
|
|
1122
|
-
textContentBase,
|
|
1123
|
-
cardContentBase,
|
|
1124
|
-
receiptContentBase
|
|
1152
|
+
blankContentBase.extend(withWash),
|
|
1153
|
+
imageContentBase.extend(withWash),
|
|
1154
|
+
textContentBase.extend(withWash),
|
|
1155
|
+
cardContentBase.extend(withWash),
|
|
1156
|
+
receiptContentBase.extend(withWash)
|
|
1125
1157
|
]);
|
|
1126
|
-
var withBack = { back: backContentSchema.optional() };
|
|
1158
|
+
var withBack = { back: backContentSchema.optional(), ...withWash };
|
|
1127
1159
|
var blankContentSchema = blankContentBase.extend(withBack);
|
|
1128
1160
|
var imageContentSchema = imageContentBase.extend(withBack);
|
|
1129
1161
|
var textContentSchema = textContentBase.extend(withBack);
|
|
@@ -1136,6 +1168,14 @@ var contentSchema = import_zod14.z.discriminatedUnion("type", [
|
|
|
1136
1168
|
cardContentSchema,
|
|
1137
1169
|
receiptContentSchema
|
|
1138
1170
|
]);
|
|
1171
|
+
var contentNames = contentSchema.options.map(
|
|
1172
|
+
(option) => option.shape.type.value
|
|
1173
|
+
);
|
|
1174
|
+
function contentSchemaFor(type) {
|
|
1175
|
+
const option = contentSchema.options.find((candidate) => candidate.shape.type.value === type);
|
|
1176
|
+
if (!option) throw new Error(`Unknown content type: ${type}`);
|
|
1177
|
+
return option;
|
|
1178
|
+
}
|
|
1139
1179
|
var paperEdges = ["top", "right", "bottom", "left"];
|
|
1140
1180
|
var surfaceSchema = import_zod14.z.object({
|
|
1141
1181
|
/** Paper fiber noise, 0..1. */
|
|
@@ -1223,8 +1263,70 @@ var lightingNames = [
|
|
|
1223
1263
|
"lightbox"
|
|
1224
1264
|
];
|
|
1225
1265
|
var filmNames = ["agx", "neutral", "filmic"];
|
|
1266
|
+
var lightSchema = import_zod14.z.object({
|
|
1267
|
+
/** Tone-mapping exposure — the stop the whole picture is printed at. */
|
|
1268
|
+
exposure: import_zod14.z.number().min(0.1).max(4).optional(),
|
|
1269
|
+
/**
|
|
1270
|
+
* The tone curve — the film, where `exposure` is the stop.
|
|
1271
|
+
*
|
|
1272
|
+
* `filmic` is ACES, which is what every preset used to be pinned to and is
|
|
1273
|
+
* kept so a scene tuned against it can say so. On near-white paper it is
|
|
1274
|
+
* the wrong film: it desaturates and drags bright neutrals toward
|
|
1275
|
+
* yellow-green, which is the sepia cast a lit sheet used to pick up.
|
|
1276
|
+
*/
|
|
1277
|
+
film: import_zod14.z.enum(filmNames).optional(),
|
|
1278
|
+
/** Key light strength. */
|
|
1279
|
+
key: import_zod14.z.number().min(0).max(12).optional(),
|
|
1280
|
+
/** Key light colour. */
|
|
1281
|
+
color: import_zod14.z.string().optional().describe("color"),
|
|
1282
|
+
/**
|
|
1283
|
+
* Where the key stands, degrees around the vertical. 0° is straight in
|
|
1284
|
+
* front of the paper (+Z, beside the camera), 90° is off to the right,
|
|
1285
|
+
* and ±180° is directly behind it — which is where `nave` puts it, and
|
|
1286
|
+
* why that preset is carried by light coming THROUGH the paper.
|
|
1287
|
+
*/
|
|
1288
|
+
direction: import_zod14.z.number().min(-180).max(180).optional(),
|
|
1289
|
+
/** How high the key stands, degrees above the horizon. */
|
|
1290
|
+
height: import_zod14.z.number().min(-30).max(89).optional(),
|
|
1291
|
+
/** Flat fill from every direction at once. Cheap, and it kills form — reach for `studio` first. */
|
|
1292
|
+
ambient: import_zod14.z.number().min(0).max(2).optional(),
|
|
1293
|
+
/** The room's own light: an environment map built from `sky`. Directional fill, and the only thing paper's sheen has to reflect. */
|
|
1294
|
+
studio: import_zod14.z.number().min(0).max(3).optional(),
|
|
1295
|
+
/** Distance haze, as a multiple of the preset's. 0 clears the air entirely; 2 halves the distance you can see. */
|
|
1296
|
+
haze: import_zod14.z.number().min(0).max(3).optional()
|
|
1297
|
+
});
|
|
1298
|
+
var backdropSchema = import_zod14.z.object({
|
|
1299
|
+
/** Behind everything, and behind the picture where it does not reach. */
|
|
1300
|
+
color: import_zod14.z.string().default("#171717").describe("color"),
|
|
1301
|
+
/** A URL, or an uploaded picture. Empty is the colour on its own. */
|
|
1302
|
+
image: import_zod14.z.string().default(""),
|
|
1303
|
+
fit: import_zod14.z.enum(["cover", "contain"]).default("cover"),
|
|
1304
|
+
/**
|
|
1305
|
+
* Toward the colour, so the paper stays the subject.
|
|
1306
|
+
*
|
|
1307
|
+
* A backdrop at full strength competes with the sheet in front of it —
|
|
1308
|
+
* which is what a real photographer solves by putting the background out
|
|
1309
|
+
* of the light, and what this solves by mixing it back toward the ground
|
|
1310
|
+
* it sits on.
|
|
1311
|
+
*/
|
|
1312
|
+
fade: import_zod14.z.number().min(0).max(1).default(0.25),
|
|
1313
|
+
/** Out of focus, for the same reason. */
|
|
1314
|
+
blur: import_zod14.z.number().min(0).max(1).default(0.2)
|
|
1315
|
+
});
|
|
1226
1316
|
var sceneSchema = import_zod14.z.object({
|
|
1227
|
-
lighting: import_zod14.z.enum(lightingNames).default("studio")
|
|
1317
|
+
lighting: import_zod14.z.enum(lightingNames).default("studio"),
|
|
1318
|
+
/** What is behind the sheet. Unset leaves the canvas alone. */
|
|
1319
|
+
backdrop: backdropSchema.optional(),
|
|
1320
|
+
/**
|
|
1321
|
+
* Overrides on the named preset — the same authorable half stage mode has
|
|
1322
|
+
* always had, and which a lone sheet had no way to reach.
|
|
1323
|
+
*
|
|
1324
|
+
* A preset was the starting point everywhere EXCEPT here: a stage could be
|
|
1325
|
+
* "nave, but the sun is lower", and a `<Paper>` could only be one of seven
|
|
1326
|
+
* rigs exactly as shipped. `<PaperLighting>` has taken these overrides all
|
|
1327
|
+
* along; nothing was passing them.
|
|
1328
|
+
*/
|
|
1329
|
+
light: lightSchema.default({})
|
|
1228
1330
|
});
|
|
1229
1331
|
var coreStateNames = ["rest", "hover", "pressed", "picked", "placed"];
|
|
1230
1332
|
var isStateName = (s) => coreStateNames.includes(s) || s.startsWith("custom:");
|
|
@@ -1357,11 +1459,11 @@ function computeSheetNormals(geometry) {
|
|
|
1357
1459
|
for (let i = 0, l = nrm.length; i < l; i += 3) {
|
|
1358
1460
|
const x = nrm[i];
|
|
1359
1461
|
const y = nrm[i + 1];
|
|
1360
|
-
const
|
|
1361
|
-
const len = Math.sqrt(x * x + y * y +
|
|
1462
|
+
const z25 = nrm[i + 2];
|
|
1463
|
+
const len = Math.sqrt(x * x + y * y + z25 * z25) || 1;
|
|
1362
1464
|
nrm[i] = x / len;
|
|
1363
1465
|
nrm[i + 1] = y / len;
|
|
1364
|
-
nrm[i + 2] =
|
|
1466
|
+
nrm[i + 2] = z25 / len;
|
|
1365
1467
|
}
|
|
1366
1468
|
normalAttr.needsUpdate = true;
|
|
1367
1469
|
}
|
|
@@ -1545,6 +1647,33 @@ var builtins = {
|
|
|
1545
1647
|
behavior: { type: "letter-fold", progress: 0.4, crease: 0.3 },
|
|
1546
1648
|
surface: { creaseLines: { angle: 0, positions: [1 / 3, 2 / 3], strength: 0.5 } }
|
|
1547
1649
|
},
|
|
1650
|
+
/**
|
|
1651
|
+
* The wash, shown rather than described.
|
|
1652
|
+
*
|
|
1653
|
+
* A `wash` is a field on every content type, which means it is reachable
|
|
1654
|
+
* from any preset and discoverable from none — a toggle three folders down
|
|
1655
|
+
* is not an argument for the feature. This is the argument: type set over
|
|
1656
|
+
* paint, on cotton, deckled, with the paper still showing through both.
|
|
1657
|
+
*/
|
|
1658
|
+
"washed-letter": {
|
|
1659
|
+
meta: { name: "Washed letter", tags: ["wash", "text"] },
|
|
1660
|
+
// Printer stock, not vellum: vellum is translucent and takes the room's
|
|
1661
|
+
// grey through the back of the sheet, which turns a wash the colour of
|
|
1662
|
+
// dishwater. Pigment needs something white behind it.
|
|
1663
|
+
sheet: { width: 1.05, height: 1.45 },
|
|
1664
|
+
stock: "printer",
|
|
1665
|
+
content: {
|
|
1666
|
+
type: "text",
|
|
1667
|
+
text: "Painted first,\nwritten after.",
|
|
1668
|
+
font: 'Georgia, "Times New Roman", serif',
|
|
1669
|
+
size: 52,
|
|
1670
|
+
valign: "center",
|
|
1671
|
+
align: "center",
|
|
1672
|
+
wash: { color: "#5b6f9a", secondary: "#c08a86", blooms: 6, spread: 0.85, intensity: 0.5, seed: 3 }
|
|
1673
|
+
},
|
|
1674
|
+
behavior: { type: "peel", progress: 0.12, corner: "bottom-right", radius: 0.3 },
|
|
1675
|
+
surface: { grain: 0.35, deckle: { edges: ["bottom", "right"], roughness: 0.45 } }
|
|
1676
|
+
},
|
|
1548
1677
|
"vintage-note": {
|
|
1549
1678
|
meta: { name: "Vintage note", tags: ["aging", "text"] },
|
|
1550
1679
|
sheet: { width: 1.1, height: 1.4 },
|
|
@@ -1985,6 +2114,109 @@ function paintCard(ctx, w, h, content, stock, dpr) {
|
|
|
1985
2114
|
}
|
|
1986
2115
|
}
|
|
1987
2116
|
|
|
2117
|
+
// src/content/wash.ts
|
|
2118
|
+
function mulberry32(seed) {
|
|
2119
|
+
let a = seed * 1831565813 + 2654435769;
|
|
2120
|
+
return () => {
|
|
2121
|
+
a |= 0;
|
|
2122
|
+
a = a + 1831565813 | 0;
|
|
2123
|
+
let t = Math.imul(a ^ a >>> 15, 1 | a);
|
|
2124
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
2125
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
function rgba(hex, alpha) {
|
|
2129
|
+
const m = /^#?([0-9a-f]{6})$/i.exec(hex.trim());
|
|
2130
|
+
const n = m ? Number.parseInt(m[1], 16) : 8421504;
|
|
2131
|
+
return `rgba(${n >> 16 & 255}, ${n >> 8 & 255}, ${n & 255}, ${Math.max(0, Math.min(1, alpha))})`;
|
|
2132
|
+
}
|
|
2133
|
+
function bloomShape(rng) {
|
|
2134
|
+
return {
|
|
2135
|
+
phase: [rng() * Math.PI * 2, rng() * Math.PI * 2, rng() * Math.PI * 2],
|
|
2136
|
+
amp: [0.18 + rng() * 0.1, 0.09 + rng() * 0.06, 0.05 + rng() * 0.04]
|
|
2137
|
+
};
|
|
2138
|
+
}
|
|
2139
|
+
function bloomPath(ctx, cx, cy, radius, shape, from = 0, to = 1) {
|
|
2140
|
+
const { phase, amp } = shape;
|
|
2141
|
+
const STEPS = Math.max(6, Math.round(72 * (to - from)));
|
|
2142
|
+
ctx.beginPath();
|
|
2143
|
+
for (let i = 0; i <= STEPS; i++) {
|
|
2144
|
+
const t = (from + (to - from) * i / STEPS) * Math.PI * 2;
|
|
2145
|
+
const wobble = amp[0] * Math.sin(2 * t + phase[0]) + amp[1] * Math.sin(3 * t + phase[1]) + amp[2] * Math.sin(5 * t + phase[2]);
|
|
2146
|
+
const r = radius * (1 + wobble);
|
|
2147
|
+
const x = cx + Math.cos(t) * r;
|
|
2148
|
+
const y = cy + Math.sin(t) * r;
|
|
2149
|
+
if (i === 0) ctx.moveTo(x, y);
|
|
2150
|
+
else ctx.lineTo(x, y);
|
|
2151
|
+
}
|
|
2152
|
+
if (from === 0 && to === 1) ctx.closePath();
|
|
2153
|
+
}
|
|
2154
|
+
function canBlur(ctx) {
|
|
2155
|
+
if (!("filter" in ctx)) return false;
|
|
2156
|
+
const before = ctx.filter;
|
|
2157
|
+
try {
|
|
2158
|
+
ctx.filter = "blur(2px)";
|
|
2159
|
+
const worked = ctx.filter !== "none" && ctx.filter !== before;
|
|
2160
|
+
ctx.filter = before;
|
|
2161
|
+
return worked;
|
|
2162
|
+
} catch {
|
|
2163
|
+
return false;
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
function paintWash(ctx, w, h, wash) {
|
|
2167
|
+
const rng = mulberry32(wash.seed + 1);
|
|
2168
|
+
const short = Math.min(w, h);
|
|
2169
|
+
const blurs = canBlur(ctx);
|
|
2170
|
+
ctx.save();
|
|
2171
|
+
ctx.globalCompositeOperation = "multiply";
|
|
2172
|
+
for (let i = 0; i < wash.blooms; i++) {
|
|
2173
|
+
const pigment = i % 2 === 0 ? wash.color : wash.secondary;
|
|
2174
|
+
const cx = (rng() * 1.5 - 0.25) * w;
|
|
2175
|
+
const cy = (rng() * 1.5 - 0.25) * h;
|
|
2176
|
+
const radius = short * wash.spread * (0.28 + rng() * 0.34);
|
|
2177
|
+
const alpha = wash.intensity * (0.35 + rng() * 0.4);
|
|
2178
|
+
const shape = bloomShape(rng);
|
|
2179
|
+
ctx.save();
|
|
2180
|
+
if (blurs) ctx.filter = `blur(${(2 + wash.bleed * 26) * (0.6 + rng() * 0.8)}px)`;
|
|
2181
|
+
const gx = cx + (rng() - 0.5) * radius * 0.5;
|
|
2182
|
+
const gy = cy + (rng() - 0.5) * radius * 0.5;
|
|
2183
|
+
const grad = ctx.createRadialGradient(gx, gy, radius * 0.05, gx, gy, radius * 1.15);
|
|
2184
|
+
grad.addColorStop(0, rgba(pigment, alpha * 0.55));
|
|
2185
|
+
grad.addColorStop(0.62, rgba(pigment, alpha));
|
|
2186
|
+
grad.addColorStop(1, rgba(pigment, alpha * 0.75));
|
|
2187
|
+
ctx.fillStyle = grad;
|
|
2188
|
+
bloomPath(ctx, cx, cy, radius, shape);
|
|
2189
|
+
ctx.fill();
|
|
2190
|
+
if (wash.edge > 0) {
|
|
2191
|
+
if (blurs) ctx.filter = `blur(${(1.5 + wash.bleed * 9) * (0.6 + rng() * 0.6)}px)`;
|
|
2192
|
+
ctx.lineWidth = Math.max(1, radius * (0.03 + wash.edge * 0.05));
|
|
2193
|
+
const arcs = 5;
|
|
2194
|
+
const overlap = 0.4 / arcs;
|
|
2195
|
+
for (let a = 0; a < arcs; a++) {
|
|
2196
|
+
ctx.strokeStyle = rgba(pigment, alpha * wash.edge * (0.15 + rng() * 1.05));
|
|
2197
|
+
bloomPath(ctx, cx, cy, radius, shape, a / arcs, (a + 1) / arcs + overlap);
|
|
2198
|
+
ctx.stroke();
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
ctx.restore();
|
|
2202
|
+
if (wash.granulation > 0) {
|
|
2203
|
+
ctx.save();
|
|
2204
|
+
bloomPath(ctx, cx, cy, radius, shape);
|
|
2205
|
+
ctx.clip();
|
|
2206
|
+
const grains = Math.round(wash.granulation * 900);
|
|
2207
|
+
ctx.fillStyle = rgba(pigment, wash.granulation * alpha * 0.5);
|
|
2208
|
+
for (let g = 0; g < grains; g++) {
|
|
2209
|
+
const a = rng() * Math.PI * 2;
|
|
2210
|
+
const d = Math.sqrt(rng()) * radius;
|
|
2211
|
+
const size = 1 + rng() * (short * 4e-3);
|
|
2212
|
+
ctx.fillRect(cx + Math.cos(a) * d, cy + Math.sin(a) * d, size, size);
|
|
2213
|
+
}
|
|
2214
|
+
ctx.restore();
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
ctx.restore();
|
|
2218
|
+
}
|
|
2219
|
+
|
|
1988
2220
|
// src/content/texture.ts
|
|
1989
2221
|
var LONG_EDGE = 1024;
|
|
1990
2222
|
var DPR = 2;
|
|
@@ -2035,6 +2267,7 @@ function renderContentToCanvas(content, sheet2, stock, image) {
|
|
|
2035
2267
|
canvas.height = h;
|
|
2036
2268
|
const ctx = canvas.getContext("2d");
|
|
2037
2269
|
paintBackground(ctx, w, h, stock);
|
|
2270
|
+
if (content.wash) paintWash(ctx, w, h, content.wash);
|
|
2038
2271
|
if (content.type === "image" && image && content.src) paintImage(ctx, w, h, image, content.fit);
|
|
2039
2272
|
if (content.type === "text") paintText(ctx, w, h, content, stock);
|
|
2040
2273
|
if (content.type === "receipt") paintReceipt(ctx, w, h, content, stock);
|
|
@@ -2907,13 +3140,13 @@ var ClothSim = class {
|
|
|
2907
3140
|
this.stillFrames = 0;
|
|
2908
3141
|
}
|
|
2909
3142
|
/** Nearest particle to a local-space point — the grab interface. */
|
|
2910
|
-
grabNearest(x, y,
|
|
3143
|
+
grabNearest(x, y, z25) {
|
|
2911
3144
|
let best = -1;
|
|
2912
3145
|
let bestDist = Infinity;
|
|
2913
3146
|
for (let i = 0; i < this.count; i++) {
|
|
2914
3147
|
const dx = this.positions[i * 3] - x;
|
|
2915
3148
|
const dy = this.positions[i * 3 + 1] - y;
|
|
2916
|
-
const dz = this.positions[i * 3 + 2] -
|
|
3149
|
+
const dz = this.positions[i * 3 + 2] - z25;
|
|
2917
3150
|
const d = dx * dx + dy * dy + dz * dz;
|
|
2918
3151
|
if (d < bestDist) {
|
|
2919
3152
|
bestDist = d;
|
|
@@ -2924,15 +3157,15 @@ var ClothSim = class {
|
|
|
2924
3157
|
this.wake();
|
|
2925
3158
|
return best;
|
|
2926
3159
|
}
|
|
2927
|
-
moveGrab(x, y,
|
|
3160
|
+
moveGrab(x, y, z25) {
|
|
2928
3161
|
if (this.grabbedIndex < 0) return;
|
|
2929
3162
|
const i3 = this.grabbedIndex * 3;
|
|
2930
3163
|
this.positions[i3] = x;
|
|
2931
3164
|
this.positions[i3 + 1] = y;
|
|
2932
|
-
this.positions[i3 + 2] =
|
|
3165
|
+
this.positions[i3 + 2] = z25;
|
|
2933
3166
|
this.prev[i3] = x;
|
|
2934
3167
|
this.prev[i3 + 1] = y;
|
|
2935
|
-
this.prev[i3 + 2] =
|
|
3168
|
+
this.prev[i3 + 2] = z25;
|
|
2936
3169
|
this.wake();
|
|
2937
3170
|
}
|
|
2938
3171
|
release() {
|
|
@@ -2968,19 +3201,19 @@ var ClothSim = class {
|
|
|
2968
3201
|
}
|
|
2969
3202
|
const x = p[i3];
|
|
2970
3203
|
const y = p[i3 + 1];
|
|
2971
|
-
const
|
|
3204
|
+
const z25 = p[i3 + 2];
|
|
2972
3205
|
const gust2 = wind * (0.55 + 0.45 * Math.sin(this.time * 1.7 + x * 2.1 + y * 1.3)) * 0.9;
|
|
2973
3206
|
const ax = gust2 * 0.25;
|
|
2974
3207
|
const az = gust2;
|
|
2975
3208
|
const vx = (x - this.prev[i3]) * damping;
|
|
2976
3209
|
const vy = (y - this.prev[i3 + 1]) * damping;
|
|
2977
|
-
const vz = (
|
|
3210
|
+
const vz = (z25 - this.prev[i3 + 2]) * damping;
|
|
2978
3211
|
this.prev[i3] = x;
|
|
2979
3212
|
this.prev[i3 + 1] = y;
|
|
2980
|
-
this.prev[i3 + 2] =
|
|
3213
|
+
this.prev[i3 + 2] = z25;
|
|
2981
3214
|
p[i3] = x + vx + ax * dt2;
|
|
2982
3215
|
p[i3 + 1] = y + vy - gravity * 3.2 * dt2;
|
|
2983
|
-
p[i3 + 2] =
|
|
3216
|
+
p[i3 + 2] = z25 + vz + az * dt2;
|
|
2984
3217
|
maxTravel = Math.max(maxTravel, vx * vx + vy * vy + vz * vz);
|
|
2985
3218
|
}
|
|
2986
3219
|
for (let iter = 0; iter < SOLVER_ITERATIONS; iter++) {
|
|
@@ -3037,7 +3270,6 @@ var THREE5 = __toESM(require("three"), 1);
|
|
|
3037
3270
|
var THREE4 = __toESM(require("three"), 1);
|
|
3038
3271
|
|
|
3039
3272
|
// src/scene/lighting.ts
|
|
3040
|
-
var import_zod22 = require("zod");
|
|
3041
3273
|
var lightingPresets = {
|
|
3042
3274
|
studio: {
|
|
3043
3275
|
id: "studio",
|
|
@@ -3198,46 +3430,14 @@ var lightingPresets = {
|
|
|
3198
3430
|
function getLightingPreset(name) {
|
|
3199
3431
|
return lightingPresets[name];
|
|
3200
3432
|
}
|
|
3201
|
-
var lightSchema = import_zod22.z.object({
|
|
3202
|
-
/** Tone-mapping exposure — the stop the whole picture is printed at. */
|
|
3203
|
-
exposure: import_zod22.z.number().min(0.1).max(4).optional(),
|
|
3204
|
-
/**
|
|
3205
|
-
* The tone curve — the film, where `exposure` is the stop.
|
|
3206
|
-
*
|
|
3207
|
-
* `filmic` is ACES, which is what every preset used to be pinned to and is
|
|
3208
|
-
* kept so a scene tuned against it can say so. On near-white paper it is
|
|
3209
|
-
* the wrong film: it desaturates and drags bright neutrals toward
|
|
3210
|
-
* yellow-green, which is the sepia cast a lit sheet used to pick up.
|
|
3211
|
-
*/
|
|
3212
|
-
film: import_zod22.z.enum(filmNames).optional(),
|
|
3213
|
-
/** Key light strength. */
|
|
3214
|
-
key: import_zod22.z.number().min(0).max(12).optional(),
|
|
3215
|
-
/** Key light colour. */
|
|
3216
|
-
color: import_zod22.z.string().optional(),
|
|
3217
|
-
/**
|
|
3218
|
-
* Where the key stands, degrees around the vertical. 0° is straight in
|
|
3219
|
-
* front of the paper (+Z, beside the camera), 90° is off to the right,
|
|
3220
|
-
* and ±180° is directly behind it — which is where `nave` puts it, and
|
|
3221
|
-
* why that preset is carried by light coming THROUGH the paper.
|
|
3222
|
-
*/
|
|
3223
|
-
direction: import_zod22.z.number().min(-180).max(180).optional(),
|
|
3224
|
-
/** How high the key stands, degrees above the horizon. */
|
|
3225
|
-
height: import_zod22.z.number().min(-30).max(89).optional(),
|
|
3226
|
-
/** Flat fill from every direction at once. Cheap, and it kills form — reach for `studio` first. */
|
|
3227
|
-
ambient: import_zod22.z.number().min(0).max(2).optional(),
|
|
3228
|
-
/** The room's own light: an environment map built from `sky`. Directional fill, and the only thing paper's sheen has to reflect. */
|
|
3229
|
-
studio: import_zod22.z.number().min(0).max(3).optional(),
|
|
3230
|
-
/** Distance haze, as a multiple of the preset's. 0 clears the air entirely; 2 halves the distance you can see. */
|
|
3231
|
-
haze: import_zod22.z.number().min(0).max(3).optional()
|
|
3232
|
-
});
|
|
3233
3433
|
var DEG6 = 180 / Math.PI;
|
|
3234
3434
|
function lightAngles(position) {
|
|
3235
|
-
const [x, y,
|
|
3236
|
-
const distance = Math.hypot(x, y,
|
|
3435
|
+
const [x, y, z25] = position;
|
|
3436
|
+
const distance = Math.hypot(x, y, z25);
|
|
3237
3437
|
if (distance < 1e-9) return { azimuth: 0, elevation: 90, distance: 0 };
|
|
3238
|
-
const ground = Math.hypot(x,
|
|
3438
|
+
const ground = Math.hypot(x, z25);
|
|
3239
3439
|
return {
|
|
3240
|
-
azimuth: ground < 1e-9 ? 0 : Math.atan2(x,
|
|
3440
|
+
azimuth: ground < 1e-9 ? 0 : Math.atan2(x, z25) * DEG6,
|
|
3241
3441
|
elevation: Math.atan2(y, ground) * DEG6,
|
|
3242
3442
|
distance
|
|
3243
3443
|
};
|
|
@@ -3335,8 +3535,8 @@ vec3 plTransmission(vec3 inkFilter) {
|
|
|
3335
3535
|
);
|
|
3336
3536
|
function translucencyValues(translucency, lighting) {
|
|
3337
3537
|
const preset = typeof lighting === "string" ? getLightingPreset(lighting) : lighting;
|
|
3338
|
-
const [x, y,
|
|
3339
|
-
const direction = new THREE4.Vector3(x, y,
|
|
3538
|
+
const [x, y, z25] = preset.key.position;
|
|
3539
|
+
const direction = new THREE4.Vector3(x, y, z25);
|
|
3340
3540
|
if (direction.lengthSq() < 1e-12) direction.set(0, 1, 0);
|
|
3341
3541
|
direction.normalize();
|
|
3342
3542
|
const color = new THREE4.Color(preset.key.color).multiplyScalar(preset.key.intensity * TRANSMISSION_GAIN);
|
|
@@ -4498,6 +4698,7 @@ var PaperMesh = (0, import_react7.forwardRef)(function PaperMesh2(props, ref) {
|
|
|
4498
4698
|
props.interactive && !isCloth && behavior?.handles?.map((h, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
4499
4699
|
"mesh",
|
|
4500
4700
|
{
|
|
4701
|
+
userData: { paperlabChrome: true },
|
|
4501
4702
|
ref: (m) => {
|
|
4502
4703
|
handleRefs.current[i] = m;
|
|
4503
4704
|
},
|
|
@@ -4641,7 +4842,7 @@ var toneMappings = {
|
|
|
4641
4842
|
neutral: THREE9.NeutralToneMapping,
|
|
4642
4843
|
filmic: THREE9.ACESFilmicToneMapping
|
|
4643
4844
|
};
|
|
4644
|
-
function
|
|
4845
|
+
function mulberry322(seed) {
|
|
4645
4846
|
let a = seed;
|
|
4646
4847
|
return () => {
|
|
4647
4848
|
a |= 0;
|
|
@@ -4675,7 +4876,7 @@ function makeGoboTexture(kind) {
|
|
|
4675
4876
|
}
|
|
4676
4877
|
ctx.restore();
|
|
4677
4878
|
} else {
|
|
4678
|
-
const rand =
|
|
4879
|
+
const rand = mulberry322(7);
|
|
4679
4880
|
for (const [count, radius, alpha] of [
|
|
4680
4881
|
[26, 70, 0.75],
|
|
4681
4882
|
[40, 38, 0.6],
|
|
@@ -4823,38 +5024,122 @@ function PaperLighting({
|
|
|
4823
5024
|
] });
|
|
4824
5025
|
}
|
|
4825
5026
|
|
|
5027
|
+
// src/scene/backdrop.tsx
|
|
5028
|
+
var THREE10 = __toESM(require("three"), 1);
|
|
5029
|
+
var import_react9 = require("react");
|
|
5030
|
+
var import_fiber3 = require("@react-three/fiber");
|
|
5031
|
+
function PaperBackdrop({ backdrop }) {
|
|
5032
|
+
const scene = (0, import_fiber3.useThree)((s) => s.scene);
|
|
5033
|
+
const size = (0, import_fiber3.useThree)((s) => s.size);
|
|
5034
|
+
const [image, setImage] = (0, import_react9.useState)(null);
|
|
5035
|
+
const src = backdrop?.image ?? "";
|
|
5036
|
+
(0, import_react9.useEffect)(() => {
|
|
5037
|
+
if (!src) {
|
|
5038
|
+
setImage(null);
|
|
5039
|
+
return;
|
|
5040
|
+
}
|
|
5041
|
+
let live = true;
|
|
5042
|
+
const img = new Image();
|
|
5043
|
+
img.crossOrigin = "anonymous";
|
|
5044
|
+
img.onload = () => live && setImage(img);
|
|
5045
|
+
img.onerror = () => live && setImage(null);
|
|
5046
|
+
img.src = src;
|
|
5047
|
+
return () => {
|
|
5048
|
+
live = false;
|
|
5049
|
+
};
|
|
5050
|
+
}, [src]);
|
|
5051
|
+
const key = JSON.stringify({ backdrop: backdrop ?? null, w: size.width, h: size.height, loaded: !!image });
|
|
5052
|
+
(0, import_react9.useEffect)(() => {
|
|
5053
|
+
if (!backdrop) return;
|
|
5054
|
+
const previous = scene.background;
|
|
5055
|
+
const texture = paintBackdrop(backdrop, size.width, size.height, image);
|
|
5056
|
+
scene.background = texture;
|
|
5057
|
+
return () => {
|
|
5058
|
+
scene.background = previous;
|
|
5059
|
+
texture.dispose();
|
|
5060
|
+
};
|
|
5061
|
+
}, [key]);
|
|
5062
|
+
return null;
|
|
5063
|
+
}
|
|
5064
|
+
function paintBackdrop(backdrop, width, height, image) {
|
|
5065
|
+
const w = Math.max(2, Math.round(width / 2));
|
|
5066
|
+
const h = Math.max(2, Math.round(height / 2));
|
|
5067
|
+
const canvas = document.createElement("canvas");
|
|
5068
|
+
canvas.width = w;
|
|
5069
|
+
canvas.height = h;
|
|
5070
|
+
const ctx = canvas.getContext("2d");
|
|
5071
|
+
ctx.fillStyle = backdrop.color;
|
|
5072
|
+
ctx.fillRect(0, 0, w, h);
|
|
5073
|
+
if (image && image.width > 0 && image.height > 0) {
|
|
5074
|
+
const scale = backdrop.fit === "cover" ? Math.max(w / image.width, h / image.height) : Math.min(w / image.width, h / image.height);
|
|
5075
|
+
const dw = image.width * scale;
|
|
5076
|
+
const dh = image.height * scale;
|
|
5077
|
+
ctx.save();
|
|
5078
|
+
if (backdrop.blur > 0) ctx.filter = `blur(${backdrop.blur * Math.min(w, h) * 0.06}px)`;
|
|
5079
|
+
ctx.drawImage(image, (w - dw) / 2, (h - dh) / 2, dw, dh);
|
|
5080
|
+
ctx.restore();
|
|
5081
|
+
if (backdrop.fade > 0) {
|
|
5082
|
+
ctx.globalAlpha = backdrop.fade;
|
|
5083
|
+
ctx.fillStyle = backdrop.color;
|
|
5084
|
+
ctx.fillRect(0, 0, w, h);
|
|
5085
|
+
ctx.globalAlpha = 1;
|
|
5086
|
+
}
|
|
5087
|
+
}
|
|
5088
|
+
const texture = new THREE10.CanvasTexture(canvas);
|
|
5089
|
+
texture.colorSpace = THREE10.SRGBColorSpace;
|
|
5090
|
+
return texture;
|
|
5091
|
+
}
|
|
5092
|
+
|
|
5093
|
+
// src/scene/release.tsx
|
|
5094
|
+
var import_fiber4 = require("@react-three/fiber");
|
|
5095
|
+
var import_react10 = require("react");
|
|
5096
|
+
function ReleaseContextOnUnmount() {
|
|
5097
|
+
const gl = (0, import_fiber4.useThree)((s) => s.gl);
|
|
5098
|
+
(0, import_react10.useEffect)(
|
|
5099
|
+
() => () => {
|
|
5100
|
+
gl.forceContextLoss();
|
|
5101
|
+
gl.dispose();
|
|
5102
|
+
},
|
|
5103
|
+
[gl]
|
|
5104
|
+
);
|
|
5105
|
+
return null;
|
|
5106
|
+
}
|
|
5107
|
+
|
|
4826
5108
|
// src/Paper.tsx
|
|
4827
5109
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
4828
|
-
var Paper = (0,
|
|
5110
|
+
var Paper = (0, import_react11.forwardRef)(function Paper2({ children, className, style, ...meshProps }, ref) {
|
|
4829
5111
|
const config = useResolvedConfig(meshProps);
|
|
4830
|
-
const webgl = (0,
|
|
5112
|
+
const webgl = (0, import_react11.useMemo)(() => typeof window === "undefined" ? true : supportsWebGL(), []);
|
|
4831
5113
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className, style: { width: "100%", height: "100%", ...style }, children: [
|
|
4832
|
-
webgl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
5114
|
+
webgl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_fiber5.Canvas, { shadows: true, camera: { position: [0, 0.35, 2.4], fov: 40 }, dpr: [1, 2], children: [
|
|
5115
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaperBackdrop, { backdrop: config.scene.backdrop }),
|
|
4833
5116
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4834
5117
|
PaperLighting,
|
|
4835
5118
|
{
|
|
4836
5119
|
preset: config.scene.lighting,
|
|
5120
|
+
light: config.scene.light,
|
|
4837
5121
|
floor: -1.05,
|
|
4838
5122
|
scale: 8,
|
|
4839
5123
|
reducedMotion: meshProps.reducedMotion
|
|
4840
5124
|
}
|
|
4841
5125
|
),
|
|
4842
5126
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaperMesh, { ref, ...meshProps }),
|
|
4843
|
-
children
|
|
5127
|
+
children,
|
|
5128
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ReleaseContextOnUnmount, {})
|
|
4844
5129
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaperFallback, { config }),
|
|
4845
5130
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PaperMirror, { config })
|
|
4846
5131
|
] });
|
|
4847
5132
|
});
|
|
4848
5133
|
|
|
4849
5134
|
// src/PaperField.tsx
|
|
4850
|
-
var
|
|
5135
|
+
var THREE16 = __toESM(require("three"), 1);
|
|
4851
5136
|
var import_gsap5 = require("gsap");
|
|
4852
|
-
var
|
|
4853
|
-
var
|
|
5137
|
+
var import_fiber8 = require("@react-three/fiber");
|
|
5138
|
+
var import_react18 = require("react");
|
|
4854
5139
|
|
|
4855
5140
|
// src/field/dropZones.tsx
|
|
4856
|
-
var
|
|
4857
|
-
var
|
|
5141
|
+
var THREE11 = __toESM(require("three"), 1);
|
|
5142
|
+
var import_react12 = require("react");
|
|
4858
5143
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
4859
5144
|
var DropZoneRegistry = class {
|
|
4860
5145
|
zones = /* @__PURE__ */ new Map();
|
|
@@ -4893,7 +5178,7 @@ var DropZoneRegistry = class {
|
|
|
4893
5178
|
for (const fn of this.listeners) fn();
|
|
4894
5179
|
}
|
|
4895
5180
|
};
|
|
4896
|
-
var DropZoneContext = (0,
|
|
5181
|
+
var DropZoneContext = (0, import_react12.createContext)(null);
|
|
4897
5182
|
var globCache = /* @__PURE__ */ new Map();
|
|
4898
5183
|
var globRegExp = (glob) => {
|
|
4899
5184
|
let re = globCache.get(glob);
|
|
@@ -4909,13 +5194,13 @@ function zoneAccepts(zone, name) {
|
|
|
4909
5194
|
}
|
|
4910
5195
|
var zoneContains = (zone, x, y) => Math.abs(x - zone.bounds.position[0]) <= zone.bounds.size[0] / 2 && Math.abs(y - zone.bounds.position[1]) <= zone.bounds.size[1] / 2;
|
|
4911
5196
|
function DropZone(props) {
|
|
4912
|
-
const registry4 = (0,
|
|
5197
|
+
const registry4 = (0, import_react12.useContext)(DropZoneContext);
|
|
4913
5198
|
const { id, accept, bounds, highlight = "glow", onPlace } = props;
|
|
4914
|
-
const place = (0,
|
|
4915
|
-
(0,
|
|
5199
|
+
const place = (0, import_react12.useRef)(onPlace);
|
|
5200
|
+
(0, import_react12.useEffect)(() => {
|
|
4916
5201
|
place.current = onPlace;
|
|
4917
5202
|
}, [onPlace]);
|
|
4918
|
-
(0,
|
|
5203
|
+
(0, import_react12.useEffect)(() => {
|
|
4919
5204
|
if (!registry4) return;
|
|
4920
5205
|
return registry4.register({
|
|
4921
5206
|
id,
|
|
@@ -4929,17 +5214,17 @@ function DropZone(props) {
|
|
|
4929
5214
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DropZoneVisual, { registry: registry4, config: { id, accept, bounds, highlight } });
|
|
4930
5215
|
}
|
|
4931
5216
|
function DropZoneVisual({ registry: registry4, config }) {
|
|
4932
|
-
(0,
|
|
5217
|
+
(0, import_react12.useSyncExternalStore)(registry4.subscribe, registry4.getVersion, registry4.getVersion);
|
|
4933
5218
|
const hovered = registry4.hovered === config.id;
|
|
4934
5219
|
const style = config.highlight ?? "glow";
|
|
4935
5220
|
const [w, h] = config.bounds.size;
|
|
4936
|
-
const edges = (0,
|
|
4937
|
-
const plane = new
|
|
4938
|
-
const geo = new
|
|
5221
|
+
const edges = (0, import_react12.useMemo)(() => {
|
|
5222
|
+
const plane = new THREE11.PlaneGeometry(w, h);
|
|
5223
|
+
const geo = new THREE11.EdgesGeometry(plane);
|
|
4939
5224
|
plane.dispose();
|
|
4940
5225
|
return geo;
|
|
4941
5226
|
}, [w, h]);
|
|
4942
|
-
(0,
|
|
5227
|
+
(0, import_react12.useEffect)(() => () => edges.dispose(), [edges]);
|
|
4943
5228
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("group", { position: config.bounds.position, children: [
|
|
4944
5229
|
style !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("mesh", { children: [
|
|
4945
5230
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("planeGeometry", { args: [w, h] }),
|
|
@@ -4958,18 +5243,18 @@ function DropZoneVisual({ registry: registry4, config }) {
|
|
|
4958
5243
|
}
|
|
4959
5244
|
|
|
4960
5245
|
// src/field/sheetGrid.ts
|
|
4961
|
-
var
|
|
4962
|
-
var sheetLayoutSchema =
|
|
4963
|
-
rows:
|
|
4964
|
-
columns:
|
|
5246
|
+
var import_zod22 = require("zod");
|
|
5247
|
+
var sheetLayoutSchema = import_zod22.z.object({
|
|
5248
|
+
rows: import_zod22.z.number().int().min(1).max(12).default(2),
|
|
5249
|
+
columns: import_zod22.z.number().int().min(1).max(12).default(5),
|
|
4965
5250
|
/** World-units gap between slots. Stamps are printed in register — no jitter. */
|
|
4966
|
-
gutter:
|
|
5251
|
+
gutter: import_zod22.z.number().min(0).max(1).default(0.08),
|
|
4967
5252
|
/** Slot footprint in world units (the paper preset should match). */
|
|
4968
|
-
cellWidth:
|
|
4969
|
-
cellHeight:
|
|
5253
|
+
cellWidth: import_zod22.z.number().min(0.1).max(4).default(0.72),
|
|
5254
|
+
cellHeight: import_zod22.z.number().min(0.1).max(4).default(0.86),
|
|
4970
5255
|
/** Render the shared backing sheet behind the grid. */
|
|
4971
|
-
backing:
|
|
4972
|
-
backingMargin:
|
|
5256
|
+
backing: import_zod22.z.boolean().default(true),
|
|
5257
|
+
backingMargin: import_zod22.z.number().min(0).max(1).default(0.12)
|
|
4973
5258
|
});
|
|
4974
5259
|
var SHEET_LIFT = 0.012;
|
|
4975
5260
|
function withSheetCellFromPaper(parsed, rawOptions, paperDims) {
|
|
@@ -5061,24 +5346,24 @@ function resolveFieldSlotConfig(slot, fallback, index, layoutId, layoutOptions)
|
|
|
5061
5346
|
}
|
|
5062
5347
|
|
|
5063
5348
|
// src/field/fieldGroup.tsx
|
|
5064
|
-
var
|
|
5349
|
+
var THREE13 = __toESM(require("three"), 1);
|
|
5065
5350
|
var import_gsap3 = require("gsap");
|
|
5066
|
-
var
|
|
5067
|
-
var
|
|
5351
|
+
var import_fiber6 = require("@react-three/fiber");
|
|
5352
|
+
var import_react14 = require("react");
|
|
5068
5353
|
var import_three_custom_shader_material2 = __toESM(require("three-custom-shader-material"), 1);
|
|
5069
5354
|
|
|
5070
5355
|
// src/content/atlas.ts
|
|
5071
|
-
var
|
|
5072
|
-
var
|
|
5356
|
+
var THREE12 = __toESM(require("three"), 1);
|
|
5357
|
+
var import_react13 = require("react");
|
|
5073
5358
|
var MAX_ATLAS = 4096;
|
|
5074
5359
|
function atlasGrid(count, aspect = 1) {
|
|
5075
5360
|
const cols = Math.max(1, Math.min(count, Math.ceil(Math.sqrt(count * Math.max(aspect, 0.01)))));
|
|
5076
5361
|
return { cols, rows: Math.max(1, Math.ceil(count / cols)) };
|
|
5077
5362
|
}
|
|
5078
5363
|
function useContentAtlas(contents, sheet2, stock) {
|
|
5079
|
-
const [atlas, setAtlas] = (0,
|
|
5364
|
+
const [atlas, setAtlas] = (0, import_react13.useState)(null);
|
|
5080
5365
|
const stableContents = useStable(contents);
|
|
5081
|
-
(0,
|
|
5366
|
+
(0, import_react13.useEffect)(() => {
|
|
5082
5367
|
let disposed = false;
|
|
5083
5368
|
const aspect = sheet2.height / sheet2.width;
|
|
5084
5369
|
const { cols, rows } = atlasGrid(contents.length, aspect);
|
|
@@ -5094,8 +5379,8 @@ function useContentAtlas(contents, sheet2, stock) {
|
|
|
5094
5379
|
const ctx = canvas.getContext("2d");
|
|
5095
5380
|
ctx.fillStyle = stock.color;
|
|
5096
5381
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
5097
|
-
const texture = new
|
|
5098
|
-
texture.colorSpace =
|
|
5382
|
+
const texture = new THREE12.CanvasTexture(canvas);
|
|
5383
|
+
texture.colorSpace = THREE12.SRGBColorSpace;
|
|
5099
5384
|
texture.anisotropy = 4;
|
|
5100
5385
|
setAtlas({ texture, cols, rows });
|
|
5101
5386
|
const drawTile = (index, tile) => {
|
|
@@ -5254,21 +5539,21 @@ function cap(s) {
|
|
|
5254
5539
|
}
|
|
5255
5540
|
|
|
5256
5541
|
// src/field/layouts/index.ts
|
|
5257
|
-
var
|
|
5542
|
+
var import_zod24 = require("zod");
|
|
5258
5543
|
|
|
5259
5544
|
// src/stage/path.ts
|
|
5260
|
-
var
|
|
5261
|
-
var walkPathSchema =
|
|
5545
|
+
var import_zod23 = require("zod");
|
|
5546
|
+
var walkPathSchema = import_zod23.z.object({
|
|
5262
5547
|
/**
|
|
5263
5548
|
* Control points on the ground plane, [x, z]. The default walks away from
|
|
5264
5549
|
* the camera down -Z — the shot every reference image is composed on.
|
|
5265
5550
|
*/
|
|
5266
|
-
points:
|
|
5551
|
+
points: import_zod23.z.array(import_zod23.z.tuple([import_zod23.z.number(), import_zod23.z.number()])).min(2).default([
|
|
5267
5552
|
[0, 9],
|
|
5268
5553
|
[0, -9]
|
|
5269
5554
|
]),
|
|
5270
5555
|
/** Join the last point back to the first: an endless walk, and the only form `phase` can slide. */
|
|
5271
|
-
closed:
|
|
5556
|
+
closed: import_zod23.z.boolean().default(false)
|
|
5272
5557
|
});
|
|
5273
5558
|
var SAMPLES_PER_SEGMENT = 24;
|
|
5274
5559
|
var EPSILON = 1e-6;
|
|
@@ -5394,9 +5679,9 @@ function jitter2(seed, i) {
|
|
|
5394
5679
|
function ramp(i, n) {
|
|
5395
5680
|
return n > 1 ? i / (n - 1) : 1;
|
|
5396
5681
|
}
|
|
5397
|
-
var ringSchema =
|
|
5398
|
-
radius:
|
|
5399
|
-
tiltDeg:
|
|
5682
|
+
var ringSchema = import_zod24.z.object({
|
|
5683
|
+
radius: import_zod24.z.number().min(0.5).max(12).default(2.6),
|
|
5684
|
+
tiltDeg: import_zod24.z.number().min(-45).max(45).default(8)
|
|
5400
5685
|
});
|
|
5401
5686
|
var ring = {
|
|
5402
5687
|
id: "ring",
|
|
@@ -5414,15 +5699,15 @@ var ring = {
|
|
|
5414
5699
|
};
|
|
5415
5700
|
}
|
|
5416
5701
|
};
|
|
5417
|
-
var fanSchema =
|
|
5702
|
+
var fanSchema = import_zod24.z.object({
|
|
5418
5703
|
/** Total angular sweep from the first sheet to the last, degrees. */
|
|
5419
|
-
sweep:
|
|
5704
|
+
sweep: import_zod24.z.number().min(0).max(180).default(72),
|
|
5420
5705
|
/** Where the shared pin sits, in half-sheet-heights below center. 1 = the bottom edge. */
|
|
5421
|
-
hinge:
|
|
5706
|
+
hinge: import_zod24.z.number().min(0).max(4).default(1.15),
|
|
5422
5707
|
/** Thickness step so the sheets stack in order instead of z-fighting. */
|
|
5423
|
-
lift:
|
|
5708
|
+
lift: import_zod24.z.number().min(2e-3).max(0.08).default(0.012),
|
|
5424
5709
|
/** How much flatter the middle of the fan sits than its outer sheets. */
|
|
5425
|
-
bow:
|
|
5710
|
+
bow: import_zod24.z.number().min(0).max(1).default(0.7)
|
|
5426
5711
|
});
|
|
5427
5712
|
var fan = {
|
|
5428
5713
|
id: "fan",
|
|
@@ -5442,16 +5727,16 @@ var fan = {
|
|
|
5442
5727
|
};
|
|
5443
5728
|
}
|
|
5444
5729
|
};
|
|
5445
|
-
var spreadSchema =
|
|
5730
|
+
var spreadSchema = import_zod24.z.object({
|
|
5446
5731
|
/** How far each sheet slides past the one below it. */
|
|
5447
|
-
slip:
|
|
5732
|
+
slip: import_zod24.z.number().min(0.02).max(2).default(0.3),
|
|
5448
5733
|
/** Direction of the slide, degrees. 0 slides right, 90 slides up. */
|
|
5449
|
-
angle:
|
|
5450
|
-
lift:
|
|
5734
|
+
angle: import_zod24.z.number().min(-180).max(180).default(28),
|
|
5735
|
+
lift: import_zod24.z.number().min(2e-3).max(0.08).default(0.012),
|
|
5451
5736
|
/** How much more the sheets at the far end of the slide bow. */
|
|
5452
|
-
bow:
|
|
5737
|
+
bow: import_zod24.z.number().min(0).max(1).default(0.6),
|
|
5453
5738
|
/** Nothing hand-slid is perfectly square — a touch of per-sheet rotation. */
|
|
5454
|
-
drift:
|
|
5739
|
+
drift: import_zod24.z.number().min(0).max(1).default(0.15)
|
|
5455
5740
|
});
|
|
5456
5741
|
var spread = {
|
|
5457
5742
|
id: "spread",
|
|
@@ -5469,15 +5754,15 @@ var spread = {
|
|
|
5469
5754
|
};
|
|
5470
5755
|
}
|
|
5471
5756
|
};
|
|
5472
|
-
var pileSchema =
|
|
5757
|
+
var pileSchema = import_zod24.z.object({
|
|
5473
5758
|
/** How far sheets wander from the center of the heap. */
|
|
5474
|
-
scatter:
|
|
5759
|
+
scatter: import_zod24.z.number().min(0).max(2).default(0.22),
|
|
5475
5760
|
/** Widest angle a sheet sits off square, degrees. */
|
|
5476
|
-
turn:
|
|
5477
|
-
lift:
|
|
5761
|
+
turn: import_zod24.z.number().min(0).max(180).default(24),
|
|
5762
|
+
lift: import_zod24.z.number().min(2e-3).max(0.08).default(0.011),
|
|
5478
5763
|
/** How flat the sheets underneath are pressed by the ones on top. */
|
|
5479
|
-
press:
|
|
5480
|
-
seed:
|
|
5764
|
+
press: import_zod24.z.number().min(0).max(1).default(0.85),
|
|
5765
|
+
seed: import_zod24.z.number().int().min(0).max(9999).default(3)
|
|
5481
5766
|
});
|
|
5482
5767
|
var pile = {
|
|
5483
5768
|
id: "pile",
|
|
@@ -5493,12 +5778,12 @@ var pile = {
|
|
|
5493
5778
|
};
|
|
5494
5779
|
}
|
|
5495
5780
|
};
|
|
5496
|
-
var wallSchema =
|
|
5497
|
-
gapX:
|
|
5498
|
-
gapY:
|
|
5499
|
-
jitterAmt:
|
|
5781
|
+
var wallSchema = import_zod24.z.object({
|
|
5782
|
+
gapX: import_zod24.z.number().min(0.05).max(1).default(0.22),
|
|
5783
|
+
gapY: import_zod24.z.number().min(0.05).max(1).default(0.3),
|
|
5784
|
+
jitterAmt: import_zod24.z.number().min(0).max(1).default(0.25),
|
|
5500
5785
|
/** Spread of sag across the wall — no two pinned sheets hang alike. */
|
|
5501
|
-
sag:
|
|
5786
|
+
sag: import_zod24.z.number().min(0).max(1).default(0.45)
|
|
5502
5787
|
});
|
|
5503
5788
|
var wall = {
|
|
5504
5789
|
id: "wall",
|
|
@@ -5524,15 +5809,15 @@ var wall = {
|
|
|
5524
5809
|
};
|
|
5525
5810
|
}
|
|
5526
5811
|
};
|
|
5527
|
-
var spillSchema =
|
|
5528
|
-
spreadX:
|
|
5529
|
-
spreadY:
|
|
5530
|
-
depth:
|
|
5812
|
+
var spillSchema = import_zod24.z.object({
|
|
5813
|
+
spreadX: import_zod24.z.number().min(0.5).max(8).default(2.4),
|
|
5814
|
+
spreadY: import_zod24.z.number().min(0.5).max(8).default(1.5),
|
|
5815
|
+
depth: import_zod24.z.number().min(0).max(6).default(1.6),
|
|
5531
5816
|
/** How far sheets pitch and roll out of the picture plane. */
|
|
5532
|
-
tumble:
|
|
5817
|
+
tumble: import_zod24.z.number().min(0).max(1).default(0.5),
|
|
5533
5818
|
/** Spread of bend across the sheets — a spill does not fold them alike. */
|
|
5534
|
-
vary:
|
|
5535
|
-
seed:
|
|
5819
|
+
vary: import_zod24.z.number().min(0).max(1).default(0.6),
|
|
5820
|
+
seed: import_zod24.z.number().int().min(0).max(9999).default(7)
|
|
5536
5821
|
});
|
|
5537
5822
|
var spill = {
|
|
5538
5823
|
id: "spill",
|
|
@@ -5557,13 +5842,13 @@ var spill = {
|
|
|
5557
5842
|
};
|
|
5558
5843
|
}
|
|
5559
5844
|
};
|
|
5560
|
-
var sweepSchema =
|
|
5561
|
-
columns:
|
|
5845
|
+
var sweepSchema = import_zod24.z.object({
|
|
5846
|
+
columns: import_zod24.z.number().int().min(1).max(24).default(5),
|
|
5562
5847
|
/** Breathing room around each specimen. */
|
|
5563
|
-
gap:
|
|
5848
|
+
gap: import_zod24.z.number().min(0).max(2).default(0.22),
|
|
5564
5849
|
/** Deformation at the first specimen and at the last. */
|
|
5565
|
-
from:
|
|
5566
|
-
to:
|
|
5850
|
+
from: import_zod24.z.number().min(0).max(1).default(0),
|
|
5851
|
+
to: import_zod24.z.number().min(0).max(1).default(1)
|
|
5567
5852
|
});
|
|
5568
5853
|
var sweep = {
|
|
5569
5854
|
id: "sweep",
|
|
@@ -5587,15 +5872,15 @@ var sweep = {
|
|
|
5587
5872
|
};
|
|
5588
5873
|
}
|
|
5589
5874
|
};
|
|
5590
|
-
var bookSchema =
|
|
5875
|
+
var bookSchema = import_zod24.z.object({
|
|
5591
5876
|
/** How far the outermost page lifts off the block, degrees. */
|
|
5592
|
-
spread:
|
|
5877
|
+
spread: import_zod24.z.number().min(0).max(150).default(55),
|
|
5593
5878
|
/** Fraction of the pages bound to the left. 0 = a one-sided sample book. */
|
|
5594
|
-
split:
|
|
5879
|
+
split: import_zod24.z.number().min(0).max(1).default(0.5),
|
|
5595
5880
|
/** Page thickness — the gap between pages of one block. */
|
|
5596
|
-
lift:
|
|
5881
|
+
lift: import_zod24.z.number().min(1e-3).max(0.05).default(8e-3),
|
|
5597
5882
|
/** How much more a lifted page arcs than one lying flat in the block. */
|
|
5598
|
-
gutter:
|
|
5883
|
+
gutter: import_zod24.z.number().min(0).max(1).default(0.6)
|
|
5599
5884
|
});
|
|
5600
5885
|
var book = {
|
|
5601
5886
|
id: "book",
|
|
@@ -5622,11 +5907,11 @@ var book = {
|
|
|
5622
5907
|
};
|
|
5623
5908
|
}
|
|
5624
5909
|
};
|
|
5625
|
-
var accordionSchema =
|
|
5910
|
+
var accordionSchema = import_zod24.z.object({
|
|
5626
5911
|
/** How far each panel tilts off the strip's line, degrees. 0 = flat, 90 = shut. */
|
|
5627
|
-
angle:
|
|
5912
|
+
angle: import_zod24.z.number().min(0).max(89).default(55),
|
|
5628
5913
|
/** A concertina holds its creases — how much bow the panels keep. */
|
|
5629
|
-
slack:
|
|
5914
|
+
slack: import_zod24.z.number().min(0).max(1).default(0.15)
|
|
5630
5915
|
});
|
|
5631
5916
|
var accordion = {
|
|
5632
5917
|
id: "accordion",
|
|
@@ -5645,16 +5930,16 @@ var accordion = {
|
|
|
5645
5930
|
};
|
|
5646
5931
|
}
|
|
5647
5932
|
};
|
|
5648
|
-
var rackSchema =
|
|
5933
|
+
var rackSchema = import_zod24.z.object({
|
|
5649
5934
|
/** Gap along the row, as a fraction of the paper's width. Under 1 they overlap. */
|
|
5650
|
-
spacing:
|
|
5935
|
+
spacing: import_zod24.z.number().min(0.05).max(2).default(0.82),
|
|
5651
5936
|
/** How far a sheet leans back off vertical, degrees. */
|
|
5652
|
-
lean:
|
|
5937
|
+
lean: import_zod24.z.number().min(0).max(70).default(16),
|
|
5653
5938
|
/** How much that lean differs sheet to sheet — nothing propped is uniform. */
|
|
5654
|
-
vary:
|
|
5939
|
+
vary: import_zod24.z.number().min(0).max(1).default(0.55),
|
|
5655
5940
|
/** Small rotations off square. */
|
|
5656
|
-
sway:
|
|
5657
|
-
seed:
|
|
5941
|
+
sway: import_zod24.z.number().min(0).max(1).default(0.35),
|
|
5942
|
+
seed: import_zod24.z.number().int().min(0).max(9999).default(5)
|
|
5658
5943
|
});
|
|
5659
5944
|
var rack = {
|
|
5660
5945
|
id: "rack",
|
|
@@ -5678,19 +5963,19 @@ var rack = {
|
|
|
5678
5963
|
};
|
|
5679
5964
|
}
|
|
5680
5965
|
};
|
|
5681
|
-
var colonnadeSchema =
|
|
5966
|
+
var colonnadeSchema = import_zod24.z.object({
|
|
5682
5967
|
/** The walk the colonnade is built along — see `stage/path`. */
|
|
5683
5968
|
path: walkPathSchema.default({}),
|
|
5684
5969
|
/** Half-width of the clear aisle: how far each banner stands off the walk line. */
|
|
5685
|
-
aisle:
|
|
5970
|
+
aisle: import_zod24.z.number().min(0.2).max(20).default(2.4),
|
|
5686
5971
|
/** How much that gap opens and closes along the walk. Nothing hung by hand is a corridor. */
|
|
5687
|
-
breathe:
|
|
5972
|
+
breathe: import_zod24.z.number().min(0).max(1).default(0.3),
|
|
5688
5973
|
/** Widest angle a banner turns off square to the aisle, degrees. */
|
|
5689
|
-
twist:
|
|
5974
|
+
twist: import_zod24.z.number().min(0).max(90).default(22),
|
|
5690
5975
|
/** Fraction of the walk left clear at each end, so the figure has somewhere to enter from. */
|
|
5691
|
-
margin:
|
|
5976
|
+
margin: import_zod24.z.number().min(0).max(0.45).default(0.05),
|
|
5692
5977
|
/** Spread of banner heights, 0..1. */
|
|
5693
|
-
rise:
|
|
5978
|
+
rise: import_zod24.z.number().min(0).max(1).default(0.28),
|
|
5694
5979
|
/**
|
|
5695
5980
|
* How far the banners lift off the floor, as a fraction of their height.
|
|
5696
5981
|
*
|
|
@@ -5704,10 +5989,10 @@ var colonnadeSchema = import_zod25.z.object({
|
|
|
5704
5989
|
* The bound used to be 0, so the one thing this option documented itself
|
|
5705
5990
|
* as doing was the one thing it could not do.
|
|
5706
5991
|
*/
|
|
5707
|
-
hover:
|
|
5992
|
+
hover: import_zod24.z.number().min(-0.5).max(1).default(0),
|
|
5708
5993
|
/** Spread of deformation — no two lengths of hung paper drape alike. */
|
|
5709
|
-
drape:
|
|
5710
|
-
seed:
|
|
5994
|
+
drape: import_zod24.z.number().min(0).max(1).default(0.5),
|
|
5995
|
+
seed: import_zod24.z.number().int().min(0).max(9999).default(2)
|
|
5711
5996
|
});
|
|
5712
5997
|
function colonnadeStop(i, n, margin) {
|
|
5713
5998
|
const side = i % 2 === 0 ? 1 : -1;
|
|
@@ -5792,7 +6077,7 @@ function fieldShapeStack(config, progress) {
|
|
|
5792
6077
|
|
|
5793
6078
|
// src/field/fieldGroup.tsx
|
|
5794
6079
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
5795
|
-
var scratchObj = new
|
|
6080
|
+
var scratchObj = new THREE13.Object3D();
|
|
5796
6081
|
var scratchAero = { position: [0, 0, 0], rotation: [0, 0, 0] };
|
|
5797
6082
|
var FIELD_SEGMENT_CAP = 48;
|
|
5798
6083
|
var FIELD_AUTO_CEILING = 128;
|
|
@@ -5811,11 +6096,11 @@ function FieldGroup({
|
|
|
5811
6096
|
const stock = getStock(config.stock);
|
|
5812
6097
|
const rig = useLightRig(config.scene.lighting);
|
|
5813
6098
|
const behavior = config.behavior && !config.deformers ? getBehavior(config.behavior.type) : null;
|
|
5814
|
-
const progressRef = (0,
|
|
6099
|
+
const progressRef = (0, import_react14.useRef)(
|
|
5815
6100
|
behavior ? config.behavior[behavior.progressParam] : 0
|
|
5816
6101
|
);
|
|
5817
6102
|
const buildStackAt = (progress) => fieldShapeStack(config, progress);
|
|
5818
|
-
const initialStack = (0,
|
|
6103
|
+
const initialStack = (0, import_react14.useMemo)(
|
|
5819
6104
|
() => buildStackAt(progressRef.current),
|
|
5820
6105
|
[
|
|
5821
6106
|
JSON.stringify(config.behavior ?? null),
|
|
@@ -5824,7 +6109,7 @@ function FieldGroup({
|
|
|
5824
6109
|
]
|
|
5825
6110
|
);
|
|
5826
6111
|
const structureKey = initialStack.map((i) => i.type).join("|");
|
|
5827
|
-
const geometry = (0,
|
|
6112
|
+
const geometry = (0, import_react14.useMemo)(() => {
|
|
5828
6113
|
const floor = stackMinSegments(initialStack, config.sheet);
|
|
5829
6114
|
const want = [0, 0];
|
|
5830
6115
|
for (const p of PROGRESS_SAMPLES2) {
|
|
@@ -5853,28 +6138,28 @@ function FieldGroup({
|
|
|
5853
6138
|
atlasIdx[i] = i;
|
|
5854
6139
|
phase[i] = indices[i] * 0.618034 % 1 * 4;
|
|
5855
6140
|
}
|
|
5856
|
-
geo.setAttribute("aAtlas", new
|
|
5857
|
-
geo.setAttribute("aPhase", new
|
|
5858
|
-
geo.setAttribute("aBias", new
|
|
6141
|
+
geo.setAttribute("aAtlas", new THREE13.InstancedBufferAttribute(atlasIdx, 1));
|
|
6142
|
+
geo.setAttribute("aPhase", new THREE13.InstancedBufferAttribute(phase, 1));
|
|
6143
|
+
geo.setAttribute("aBias", new THREE13.InstancedBufferAttribute(bias, 1));
|
|
5859
6144
|
return geo;
|
|
5860
6145
|
}, [JSON.stringify(config.sheet), structureKey, count, autoCeiling]);
|
|
5861
|
-
(0,
|
|
6146
|
+
(0, import_react14.useEffect)(() => () => geometry.dispose(), [geometry]);
|
|
5862
6147
|
const atlas = useContentAtlas(contents, config.sheet, stock);
|
|
5863
|
-
const shader = (0,
|
|
6148
|
+
const shader = (0, import_react14.useMemo)(() => {
|
|
5864
6149
|
const composed = buildDisplacementGLSL(initialStack, config.sheet);
|
|
5865
6150
|
const uniforms = {};
|
|
5866
6151
|
for (const [name, value] of Object.entries(composed.uniforms)) {
|
|
5867
6152
|
uniforms[name] = {
|
|
5868
|
-
value: Array.isArray(value) && value.length === 2 ? new
|
|
6153
|
+
value: Array.isArray(value) && value.length === 2 ? new THREE13.Vector2(...value) : value
|
|
5869
6154
|
};
|
|
5870
6155
|
}
|
|
5871
6156
|
uniforms.uPlTime = { value: 0 };
|
|
5872
6157
|
uniforms.uAtlas = { value: null };
|
|
5873
|
-
uniforms.uAtlasGrid = { value: new
|
|
6158
|
+
uniforms.uAtlasGrid = { value: new THREE13.Vector2(1, 1) };
|
|
5874
6159
|
uniforms.uBackDarken = {
|
|
5875
6160
|
value: 1 - Math.min(0.45, 0.12 + config.sheet.thickness * 0.9) * stock.opacity
|
|
5876
6161
|
};
|
|
5877
|
-
uniforms.uStockColor = { value: new
|
|
6162
|
+
uniforms.uStockColor = { value: new THREE13.Color(stock.color) };
|
|
5878
6163
|
uniforms.uShowThrough = { value: config.surface.showThrough ?? stock.showThrough };
|
|
5879
6164
|
Object.assign(uniforms, translucencyUniforms(config.surface.translucency ?? stock.translucency, rig));
|
|
5880
6165
|
return {
|
|
@@ -5889,19 +6174,19 @@ function FieldGroup({
|
|
|
5889
6174
|
config.surface.showThrough,
|
|
5890
6175
|
config.surface.translucency
|
|
5891
6176
|
]);
|
|
5892
|
-
(0,
|
|
6177
|
+
(0, import_react14.useEffect)(() => {
|
|
5893
6178
|
const values = translucencyValues(config.surface.translucency ?? stock.translucency, rig);
|
|
5894
6179
|
shader.uniforms.uTranslucency.value = values.translucency;
|
|
5895
6180
|
shader.uniforms.uBackLightDir.value.copy(values.direction);
|
|
5896
6181
|
shader.uniforms.uBackLightColor.value.copy(values.color);
|
|
5897
6182
|
shader.uniforms.uAmbientTransmission.value = values.ambient;
|
|
5898
6183
|
}, [shader, rig, config.surface.translucency, stock.translucency]);
|
|
5899
|
-
(0,
|
|
6184
|
+
(0, import_react14.useEffect)(() => {
|
|
5900
6185
|
if (!atlas) return;
|
|
5901
6186
|
shader.uniforms.uAtlas.value = atlas.texture;
|
|
5902
6187
|
shader.uniforms.uAtlasGrid.value.set(atlas.cols, atlas.rows);
|
|
5903
6188
|
}, [atlas, shader]);
|
|
5904
|
-
(0,
|
|
6189
|
+
(0, import_react14.useEffect)(() => {
|
|
5905
6190
|
if (!behavior || shared.reduced) return;
|
|
5906
6191
|
const state = { p: progressRef.current };
|
|
5907
6192
|
const tween = import_gsap3.gsap.to(state, {
|
|
@@ -5918,8 +6203,8 @@ function FieldGroup({
|
|
|
5918
6203
|
tween.kill();
|
|
5919
6204
|
};
|
|
5920
6205
|
}, [structureKey, shared.reduced]);
|
|
5921
|
-
const meshRef = (0,
|
|
5922
|
-
(0,
|
|
6206
|
+
const meshRef = (0, import_react14.useRef)(null);
|
|
6207
|
+
(0, import_fiber6.useFrame)(({ clock }) => {
|
|
5923
6208
|
const mesh = meshRef.current;
|
|
5924
6209
|
if (!mesh) return;
|
|
5925
6210
|
const elapsed = clock.elapsedTime - Math.max(shared.mountTimeRef.current, 0);
|
|
@@ -5929,7 +6214,7 @@ function FieldGroup({
|
|
|
5929
6214
|
for (const [name, value] of Object.entries(values)) {
|
|
5930
6215
|
const uniform = shader.uniforms[name];
|
|
5931
6216
|
if (!uniform) continue;
|
|
5932
|
-
if (uniform.value instanceof
|
|
6217
|
+
if (uniform.value instanceof THREE13.Vector2 && Array.isArray(value)) {
|
|
5933
6218
|
uniform.value.set(value[0], value[1]);
|
|
5934
6219
|
} else {
|
|
5935
6220
|
uniform.value = value;
|
|
@@ -6013,13 +6298,13 @@ function FieldGroup({
|
|
|
6013
6298
|
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
6014
6299
|
import_three_custom_shader_material2.default,
|
|
6015
6300
|
{
|
|
6016
|
-
baseMaterial:
|
|
6301
|
+
baseMaterial: THREE13.MeshStandardMaterial,
|
|
6017
6302
|
vertexShader: shader.vertexShader,
|
|
6018
6303
|
fragmentShader: shader.fragmentShader,
|
|
6019
6304
|
uniforms: shader.uniforms,
|
|
6020
6305
|
roughness: stock.roughness,
|
|
6021
6306
|
metalness: 0,
|
|
6022
|
-
side:
|
|
6307
|
+
side: THREE13.DoubleSide
|
|
6023
6308
|
},
|
|
6024
6309
|
`${structureKey}:${count}`
|
|
6025
6310
|
)
|
|
@@ -6065,8 +6350,8 @@ var easeOut = (t) => 1 - (1 - t) ** 3;
|
|
|
6065
6350
|
var easeInOut = (t) => t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;
|
|
6066
6351
|
|
|
6067
6352
|
// src/field/backingSheet.tsx
|
|
6068
|
-
var
|
|
6069
|
-
var
|
|
6353
|
+
var THREE14 = __toESM(require("three"), 1);
|
|
6354
|
+
var import_react15 = require("react");
|
|
6070
6355
|
|
|
6071
6356
|
// src/content/backing.ts
|
|
6072
6357
|
function silhouetteRects(o, count) {
|
|
@@ -6139,7 +6424,7 @@ function BackingSheet({
|
|
|
6139
6424
|
removed
|
|
6140
6425
|
}) {
|
|
6141
6426
|
const { width, height } = sheetBackingSize(options);
|
|
6142
|
-
const canvas = (0,
|
|
6427
|
+
const canvas = (0, import_react15.useMemo)(() => {
|
|
6143
6428
|
if (typeof document === "undefined") return null;
|
|
6144
6429
|
const c = document.createElement("canvas");
|
|
6145
6430
|
const scale = Math.min(1024, Math.round(360 * Math.max(width, height))) / Math.max(width, height);
|
|
@@ -6147,14 +6432,14 @@ function BackingSheet({
|
|
|
6147
6432
|
c.height = Math.max(2, Math.round(height * scale));
|
|
6148
6433
|
return c;
|
|
6149
6434
|
}, [width, height]);
|
|
6150
|
-
const texture = (0,
|
|
6435
|
+
const texture = (0, import_react15.useMemo)(() => canvas ? new THREE14.CanvasTexture(canvas) : null, [canvas]);
|
|
6151
6436
|
const removedKey = [...removed].sort((a, b) => a - b).join(",");
|
|
6152
|
-
(0,
|
|
6437
|
+
(0, import_react15.useEffect)(() => {
|
|
6153
6438
|
if (!canvas || !texture) return;
|
|
6154
6439
|
drawBacking(canvas, { options, count, tint: BACKING_TINT, removed });
|
|
6155
6440
|
texture.needsUpdate = true;
|
|
6156
6441
|
}, [canvas, texture, JSON.stringify(options), count, removedKey]);
|
|
6157
|
-
(0,
|
|
6442
|
+
(0, import_react15.useEffect)(() => () => texture?.dispose(), [texture]);
|
|
6158
6443
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("mesh", { receiveShadow: true, children: [
|
|
6159
6444
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("planeGeometry", { args: [width, height] }),
|
|
6160
6445
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("meshStandardMaterial", { map: texture, color: "#ffffff", roughness: 0.92, metalness: 0 })
|
|
@@ -6162,10 +6447,10 @@ function BackingSheet({
|
|
|
6162
6447
|
}
|
|
6163
6448
|
|
|
6164
6449
|
// src/field/interactiveField.tsx
|
|
6165
|
-
var
|
|
6450
|
+
var THREE15 = __toESM(require("three"), 1);
|
|
6166
6451
|
var import_gsap4 = require("gsap");
|
|
6167
|
-
var
|
|
6168
|
-
var
|
|
6452
|
+
var import_fiber7 = require("@react-three/fiber");
|
|
6453
|
+
var import_react16 = require("react");
|
|
6169
6454
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
6170
6455
|
var PICK_BEHAVIORS = /* @__PURE__ */ new Set(["peel", "carry"]);
|
|
6171
6456
|
function InteractiveField(props) {
|
|
@@ -6173,15 +6458,15 @@ function InteractiveField(props) {
|
|
|
6173
6458
|
const total = papers.length;
|
|
6174
6459
|
const layout = getLayout(layoutId);
|
|
6175
6460
|
const reduced = usePrefersReducedMotion(props.reducedMotion);
|
|
6176
|
-
const contextRegistry = (0,
|
|
6177
|
-
const registry4 = (0,
|
|
6178
|
-
const camera = (0,
|
|
6179
|
-
const gl = (0,
|
|
6180
|
-
const controls = (0,
|
|
6181
|
-
const [removed, setRemoved] = (0,
|
|
6182
|
-
const [slotPatches, setSlotPatches] = (0,
|
|
6183
|
-
const [slotStates, setSlotStates] = (0,
|
|
6184
|
-
const slotConfigs = (0,
|
|
6461
|
+
const contextRegistry = (0, import_react16.useContext)(DropZoneContext);
|
|
6462
|
+
const registry4 = (0, import_react16.useMemo)(() => contextRegistry ?? new DropZoneRegistry(), [contextRegistry]);
|
|
6463
|
+
const camera = (0, import_fiber7.useThree)((s) => s.camera);
|
|
6464
|
+
const gl = (0, import_fiber7.useThree)((s) => s.gl);
|
|
6465
|
+
const controls = (0, import_fiber7.useThree)((s) => s.controls);
|
|
6466
|
+
const [removed, setRemoved] = (0, import_react16.useState)(EMPTY_SET);
|
|
6467
|
+
const [slotPatches, setSlotPatches] = (0, import_react16.useState)({});
|
|
6468
|
+
const [slotStates, setSlotStates] = (0, import_react16.useState)({});
|
|
6469
|
+
const slotConfigs = (0, import_react16.useMemo)(
|
|
6185
6470
|
() => papers.map((slot, i) => {
|
|
6186
6471
|
const config = resolveFieldSlotConfig(slot, fallback, i, layoutId, layoutOptions);
|
|
6187
6472
|
const patch = slotPatches[i];
|
|
@@ -6195,14 +6480,14 @@ function InteractiveField(props) {
|
|
|
6195
6480
|
slotPatches
|
|
6196
6481
|
]
|
|
6197
6482
|
);
|
|
6198
|
-
const poses = (0,
|
|
6483
|
+
const poses = (0, import_react16.useMemo)(
|
|
6199
6484
|
() => slotConfigs.map((_, i) => layout.pose(i, total, layoutOptions, 0, props.sheet)),
|
|
6200
6485
|
[layoutId, JSON.stringify(layoutOptions), total, props.sheet.width, props.sheet.height]
|
|
6201
6486
|
);
|
|
6202
|
-
const groupRefs = (0,
|
|
6203
|
-
const handleRefs = (0,
|
|
6204
|
-
const pressRef = (0,
|
|
6205
|
-
const carriedRef = (0,
|
|
6487
|
+
const groupRefs = (0, import_react16.useRef)([]);
|
|
6488
|
+
const handleRefs = (0, import_react16.useRef)([]);
|
|
6489
|
+
const pressRef = (0, import_react16.useRef)(null);
|
|
6490
|
+
const carriedRef = (0, import_react16.useRef)(null);
|
|
6206
6491
|
const zonesLive = () => [...props.zones ?? [], ...registry4.list()];
|
|
6207
6492
|
const slotName = (i) => {
|
|
6208
6493
|
const preset = papers[i]?.preset ?? fallback;
|
|
@@ -6215,10 +6500,10 @@ function InteractiveField(props) {
|
|
|
6215
6500
|
}
|
|
6216
6501
|
props.onSlotStateChange?.(i, state);
|
|
6217
6502
|
};
|
|
6218
|
-
const raycaster = (0,
|
|
6219
|
-
const planeScratch = (0,
|
|
6220
|
-
const pointScratch = (0,
|
|
6221
|
-
const ndcScratch = (0,
|
|
6503
|
+
const raycaster = (0, import_react16.useMemo)(() => new THREE15.Raycaster(), []);
|
|
6504
|
+
const planeScratch = (0, import_react16.useMemo)(() => new THREE15.Plane(new THREE15.Vector3(0, 0, 1), 0), []);
|
|
6505
|
+
const pointScratch = (0, import_react16.useMemo)(() => new THREE15.Vector3(), []);
|
|
6506
|
+
const ndcScratch = (0, import_react16.useMemo)(() => new THREE15.Vector2(), []);
|
|
6222
6507
|
const planePoint = (clientX, clientY, planeZ) => {
|
|
6223
6508
|
const rect = gl.domElement.getBoundingClientRect();
|
|
6224
6509
|
ndcScratch.set(
|
|
@@ -6348,7 +6633,7 @@ function InteractiveField(props) {
|
|
|
6348
6633
|
import_gsap4.gsap.to(group.rotation, { x: home.rotation[0], y: home.rotation[1], z: home.rotation[2], duration });
|
|
6349
6634
|
import_gsap4.gsap.to(group.scale, { x: home.scale, y: home.scale, z: home.scale, duration: duration * 0.5 });
|
|
6350
6635
|
};
|
|
6351
|
-
const onPointerMoveRef = (0,
|
|
6636
|
+
const onPointerMoveRef = (0, import_react16.useRef)(() => {
|
|
6352
6637
|
});
|
|
6353
6638
|
onPointerMoveRef.current = (e) => {
|
|
6354
6639
|
const press = pressRef.current;
|
|
@@ -6372,7 +6657,7 @@ function InteractiveField(props) {
|
|
|
6372
6657
|
pick(slot, hit[0], hit[1], pointerId);
|
|
6373
6658
|
}
|
|
6374
6659
|
};
|
|
6375
|
-
const onPointerUpRef = (0,
|
|
6660
|
+
const onPointerUpRef = (0, import_react16.useRef)(() => {
|
|
6376
6661
|
});
|
|
6377
6662
|
onPointerUpRef.current = (e) => {
|
|
6378
6663
|
pressRef.current = null;
|
|
@@ -6381,12 +6666,12 @@ function InteractiveField(props) {
|
|
|
6381
6666
|
if (carried.pointerId !== null && e.pointerId !== carried.pointerId) return;
|
|
6382
6667
|
const name = slotName(carried.slot);
|
|
6383
6668
|
const zone = zonesLive().find(
|
|
6384
|
-
(
|
|
6669
|
+
(z25) => zoneContains(z25, carried.x.value, carried.y.value) && zoneAccepts(z25, name)
|
|
6385
6670
|
);
|
|
6386
6671
|
if (zone) settleInto(carried, zone);
|
|
6387
6672
|
else returnHome(carried);
|
|
6388
6673
|
};
|
|
6389
|
-
(0,
|
|
6674
|
+
(0, import_react16.useEffect)(() => {
|
|
6390
6675
|
const move = (e) => onPointerMoveRef.current(e);
|
|
6391
6676
|
const up = (e) => onPointerUpRef.current(e);
|
|
6392
6677
|
window.addEventListener("pointermove", move);
|
|
@@ -6396,7 +6681,7 @@ function InteractiveField(props) {
|
|
|
6396
6681
|
window.removeEventListener("pointerup", up);
|
|
6397
6682
|
};
|
|
6398
6683
|
}, []);
|
|
6399
|
-
(0,
|
|
6684
|
+
(0, import_fiber7.useFrame)((_, delta) => {
|
|
6400
6685
|
const carried = carriedRef.current;
|
|
6401
6686
|
if (!carried || carried.settling) return;
|
|
6402
6687
|
const group = groupRefs.current[carried.slot];
|
|
@@ -6422,19 +6707,19 @@ function InteractiveField(props) {
|
|
|
6422
6707
|
group.position.z = carried.homePose.position[2] + 0.15;
|
|
6423
6708
|
handle.set("drive", carryDrive(speed));
|
|
6424
6709
|
const lag = 0.25;
|
|
6425
|
-
group.rotation.y += (
|
|
6426
|
-
group.rotation.x += (
|
|
6710
|
+
group.rotation.y += (THREE15.MathUtils.clamp(-vx * lag, -0.6, 0.6) - group.rotation.y) * 0.12;
|
|
6711
|
+
group.rotation.x += (THREE15.MathUtils.clamp(vy * lag * 0.7, -0.5, 0.5) - group.rotation.x) * 0.12;
|
|
6427
6712
|
}
|
|
6428
6713
|
const name = slotName(carried.slot);
|
|
6429
6714
|
const zone = zonesLive().find(
|
|
6430
|
-
(
|
|
6715
|
+
(z25) => zoneContains(z25, group.position.x, group.position.y) && zoneAccepts(z25, name)
|
|
6431
6716
|
);
|
|
6432
6717
|
registry4.setHovered(zone?.id ?? null);
|
|
6433
6718
|
const targetScale = (zone ? 1.03 : 1) * carried.homePose.scale;
|
|
6434
6719
|
group.scale.x += (targetScale - group.scale.x) * 0.15;
|
|
6435
6720
|
group.scale.y = group.scale.z = group.scale.x;
|
|
6436
6721
|
});
|
|
6437
|
-
const a11yImplRef = (0,
|
|
6722
|
+
const a11yImplRef = (0, import_react16.useRef)(null);
|
|
6438
6723
|
a11yImplRef.current = {
|
|
6439
6724
|
pick: (slot) => {
|
|
6440
6725
|
const home = poses[slot];
|
|
@@ -6443,7 +6728,7 @@ function InteractiveField(props) {
|
|
|
6443
6728
|
},
|
|
6444
6729
|
placeAtZone: (slot, zoneId) => {
|
|
6445
6730
|
const carried = carriedRef.current;
|
|
6446
|
-
const zone = zonesLive().find((
|
|
6731
|
+
const zone = zonesLive().find((z25) => z25.id === zoneId);
|
|
6447
6732
|
const group = groupRefs.current[slot];
|
|
6448
6733
|
if (!carried || carried.slot !== slot || !zone || !group) return;
|
|
6449
6734
|
group.position.x = zone.bounds.position[0];
|
|
@@ -6456,10 +6741,10 @@ function InteractiveField(props) {
|
|
|
6456
6741
|
const carried = carriedRef.current;
|
|
6457
6742
|
if (carried && carried.slot === slot && !carried.settling) returnHome(carried);
|
|
6458
6743
|
},
|
|
6459
|
-
zoneIds: () => zonesLive().map((
|
|
6744
|
+
zoneIds: () => zonesLive().map((z25) => z25.id),
|
|
6460
6745
|
slotState: (slot) => slotStates[slot] ?? "rest"
|
|
6461
6746
|
};
|
|
6462
|
-
(0,
|
|
6747
|
+
(0, import_react16.useEffect)(() => {
|
|
6463
6748
|
const ref = props.a11yRef;
|
|
6464
6749
|
if (!ref) return;
|
|
6465
6750
|
ref.current = {
|
|
@@ -6512,7 +6797,7 @@ function InteractiveField(props) {
|
|
|
6512
6797
|
}
|
|
6513
6798
|
|
|
6514
6799
|
// src/field/keyboardMirror.tsx
|
|
6515
|
-
var
|
|
6800
|
+
var import_react17 = require("react");
|
|
6516
6801
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
6517
6802
|
function fieldKeyboardStep(carry2, slot, key, controller) {
|
|
6518
6803
|
if (!carry2) {
|
|
@@ -6558,7 +6843,7 @@ function FieldKeyboardMirror({
|
|
|
6558
6843
|
papers,
|
|
6559
6844
|
controller
|
|
6560
6845
|
}) {
|
|
6561
|
-
const [carrying, setCarrying] = (0,
|
|
6846
|
+
const [carrying, setCarrying] = (0, import_react17.useState)(null);
|
|
6562
6847
|
const paperLabel = (slot, i) => {
|
|
6563
6848
|
try {
|
|
6564
6849
|
const config = resolveConfig({ preset: slot.preset });
|
|
@@ -6661,16 +6946,16 @@ function fitCamera(layout, n, options, sheet2, fovDeg, aspect, margin = 1.06) {
|
|
|
6661
6946
|
|
|
6662
6947
|
// src/PaperField.tsx
|
|
6663
6948
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
6664
|
-
var PaperFieldMesh = (0,
|
|
6949
|
+
var PaperFieldMesh = (0, import_react18.forwardRef)(
|
|
6665
6950
|
function PaperFieldMesh2(props, ref) {
|
|
6666
6951
|
const reduced = usePrefersReducedMotion(props.reducedMotion);
|
|
6667
|
-
const papers = (0,
|
|
6952
|
+
const papers = (0, import_react18.useMemo)(
|
|
6668
6953
|
() => effectiveFieldPapers(props.papers, props.images),
|
|
6669
6954
|
[props.papers, props.images]
|
|
6670
6955
|
);
|
|
6671
6956
|
const stablePapers = useStable(papers);
|
|
6672
6957
|
const stablePreset = useStable(props.preset ?? null);
|
|
6673
|
-
const groups = (0,
|
|
6958
|
+
const groups = (0, import_react18.useMemo)(
|
|
6674
6959
|
() => groupFieldPapers(stablePapers, stablePreset ?? void 0),
|
|
6675
6960
|
[stablePapers, stablePreset]
|
|
6676
6961
|
);
|
|
@@ -6679,22 +6964,22 @@ var PaperFieldMesh = (0, import_react16.forwardRef)(
|
|
|
6679
6964
|
const layout = getLayout(layoutId);
|
|
6680
6965
|
const firstSheet = groups[0]?.config.sheet;
|
|
6681
6966
|
const stableLayoutOptions = useStable(props.layoutOptions ?? {});
|
|
6682
|
-
const layoutOptions = (0,
|
|
6967
|
+
const layoutOptions = (0, import_react18.useMemo)(
|
|
6683
6968
|
// Sheet grids size their cells from the papers themselves — gutter is
|
|
6684
6969
|
// then literally the spacing between stamps (explicit cell dims win).
|
|
6685
6970
|
() => resolveLayoutOptions(layoutId, layout, stableLayoutOptions, firstSheet),
|
|
6686
6971
|
[layoutId, stableLayoutOptions, firstSheet?.width, firstSheet?.height]
|
|
6687
6972
|
);
|
|
6688
|
-
const phaseRef = (0,
|
|
6689
|
-
const dragVelRef = (0,
|
|
6690
|
-
const mountTimeRef = (0,
|
|
6691
|
-
const morphRef = (0,
|
|
6692
|
-
const prevLayout = (0,
|
|
6693
|
-
const gl = (0,
|
|
6973
|
+
const phaseRef = (0, import_react18.useRef)(0);
|
|
6974
|
+
const dragVelRef = (0, import_react18.useRef)(0);
|
|
6975
|
+
const mountTimeRef = (0, import_react18.useRef)(-1);
|
|
6976
|
+
const morphRef = (0, import_react18.useRef)({ from: null, t: 1 });
|
|
6977
|
+
const prevLayout = (0, import_react18.useRef)({ id: layoutId, options: layoutOptions });
|
|
6978
|
+
const gl = (0, import_fiber8.useThree)((s) => s.gl);
|
|
6694
6979
|
const driver = reduced ? "none" : props.motion?.driver ?? "autoplay";
|
|
6695
6980
|
const speed = props.motion?.speed ?? 0.5;
|
|
6696
6981
|
const entranceType = reduced ? "none" : props.entrance?.type ?? "rise";
|
|
6697
|
-
(0,
|
|
6982
|
+
(0, import_react18.useEffect)(() => {
|
|
6698
6983
|
const prev = prevLayout.current;
|
|
6699
6984
|
if (prev.id !== layoutId || JSON.stringify(prev.options) !== JSON.stringify(layoutOptions)) {
|
|
6700
6985
|
morphRef.current = { from: prev, t: 0 };
|
|
@@ -6702,7 +6987,7 @@ var PaperFieldMesh = (0, import_react16.forwardRef)(
|
|
|
6702
6987
|
prevLayout.current = { id: layoutId, options: layoutOptions };
|
|
6703
6988
|
}
|
|
6704
6989
|
}, [layoutId, layoutOptions]);
|
|
6705
|
-
(0,
|
|
6990
|
+
(0, import_react18.useEffect)(() => {
|
|
6706
6991
|
if (driver !== "drag") return;
|
|
6707
6992
|
const el = gl.domElement;
|
|
6708
6993
|
let lastX = null;
|
|
@@ -6728,7 +7013,7 @@ var PaperFieldMesh = (0, import_react16.forwardRef)(
|
|
|
6728
7013
|
window.removeEventListener("pointerup", up);
|
|
6729
7014
|
};
|
|
6730
7015
|
}, [driver, gl]);
|
|
6731
|
-
(0,
|
|
7016
|
+
(0, import_fiber8.useFrame)(({ clock }, delta) => {
|
|
6732
7017
|
if (mountTimeRef.current < 0) mountTimeRef.current = clock.elapsedTime;
|
|
6733
7018
|
if (driver === "autoplay") phaseRef.current += delta * speed * 0.02;
|
|
6734
7019
|
if (driver === "drag") {
|
|
@@ -6786,14 +7071,14 @@ var PaperFieldMesh = (0, import_react16.forwardRef)(
|
|
|
6786
7071
|
}
|
|
6787
7072
|
);
|
|
6788
7073
|
function FitCamera(meshProps) {
|
|
6789
|
-
const camera = (0,
|
|
6790
|
-
const width = (0,
|
|
6791
|
-
const height = (0,
|
|
7074
|
+
const camera = (0, import_fiber8.useThree)((s) => s.camera);
|
|
7075
|
+
const width = (0, import_fiber8.useThree)((s) => s.size.width);
|
|
7076
|
+
const height = (0, import_fiber8.useThree)((s) => s.size.height);
|
|
6792
7077
|
const papersProp = useStable(meshProps.papers ?? null);
|
|
6793
7078
|
const imagesProp = useStable(meshProps.images ?? null);
|
|
6794
7079
|
const presetProp = useStable(meshProps.preset ?? null);
|
|
6795
7080
|
const optionsProp = useStable(meshProps.layoutOptions ?? {});
|
|
6796
|
-
const field = (0,
|
|
7081
|
+
const field = (0, import_react18.useMemo)(() => {
|
|
6797
7082
|
const papers = effectiveFieldPapers(papersProp ?? void 0, imagesProp ?? void 0);
|
|
6798
7083
|
const layoutId = meshProps.layout ?? "ring";
|
|
6799
7084
|
const layout = getLayout(layoutId);
|
|
@@ -6801,8 +7086,8 @@ function FitCamera(meshProps) {
|
|
|
6801
7086
|
const options = resolveLayoutOptions(layoutId, layout, optionsProp, sheet2);
|
|
6802
7087
|
return { layout, n: papers.length, options, sheet: sheet2 ?? DEFAULT_SHEET };
|
|
6803
7088
|
}, [papersProp, imagesProp, presetProp, meshProps.layout, optionsProp]);
|
|
6804
|
-
(0,
|
|
6805
|
-
if (!(camera instanceof
|
|
7089
|
+
(0, import_react18.useEffect)(() => {
|
|
7090
|
+
if (!(camera instanceof THREE16.PerspectiveCamera)) return;
|
|
6806
7091
|
const { position, target } = fitCamera(
|
|
6807
7092
|
field.layout,
|
|
6808
7093
|
field.n,
|
|
@@ -6817,35 +7102,28 @@ function FitCamera(meshProps) {
|
|
|
6817
7102
|
}, [camera, width, height, field]);
|
|
6818
7103
|
return null;
|
|
6819
7104
|
}
|
|
6820
|
-
var PaperField = (0,
|
|
6821
|
-
const
|
|
6822
|
-
const
|
|
6823
|
-
const
|
|
7105
|
+
var PaperField = (0, import_react18.forwardRef)(function PaperField2({ children, className, style, scene, ...meshProps }, ref) {
|
|
7106
|
+
const rig = sceneSchema.parse(scene ?? {});
|
|
7107
|
+
const registry4 = (0, import_react18.useMemo)(() => new DropZoneRegistry(), []);
|
|
7108
|
+
const a11yRef = (0, import_react18.useRef)(null);
|
|
7109
|
+
const papers = (0, import_react18.useMemo)(
|
|
6824
7110
|
() => effectiveFieldPapers(meshProps.papers, meshProps.images),
|
|
6825
7111
|
[meshProps.papers, meshProps.images]
|
|
6826
7112
|
);
|
|
6827
7113
|
const stablePapers = useStable(papers);
|
|
6828
7114
|
const stablePreset = useStable(meshProps.preset ?? null);
|
|
6829
|
-
const interactive = (0,
|
|
7115
|
+
const interactive = (0, import_react18.useMemo)(
|
|
6830
7116
|
() => fieldIsInteractive(stablePapers, stablePreset ?? void 0, meshProps.interactive),
|
|
6831
7117
|
[stablePapers, stablePreset, meshProps.interactive]
|
|
6832
7118
|
);
|
|
6833
7119
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className, style: { width: "100%", height: "100%", ...style }, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(DropZoneContext.Provider, { value: registry4, children: [
|
|
6834
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
7120
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_fiber8.Canvas, { shadows: true, camera: { position: [0, 0.6, 5.2], fov: 45 }, dpr: [1, 2], children: [
|
|
6835
7121
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FitCamera, { ...meshProps }),
|
|
6836
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
6837
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
6838
|
-
"directionalLight",
|
|
6839
|
-
{
|
|
6840
|
-
position: [3, 5, 4],
|
|
6841
|
-
intensity: 1.4,
|
|
6842
|
-
castShadow: true,
|
|
6843
|
-
"shadow-mapSize": [1024, 1024],
|
|
6844
|
-
"shadow-normalBias": 0.05
|
|
6845
|
-
}
|
|
6846
|
-
),
|
|
7122
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PaperBackdrop, { backdrop: rig.backdrop }),
|
|
7123
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PaperLighting, { preset: rig.lighting, light: rig.light, floor: -2.4, scale: 14 }),
|
|
6847
7124
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PaperFieldMesh, { ref, a11yControllerRef: a11yRef, ...meshProps }),
|
|
6848
|
-
children
|
|
7125
|
+
children,
|
|
7126
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ReleaseContextOnUnmount, {})
|
|
6849
7127
|
] }),
|
|
6850
7128
|
interactive && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FieldKeyboardMirror, { papers, controller: a11yRef })
|
|
6851
7129
|
] }) });
|
|
@@ -6895,7 +7173,8 @@ function diffConfig(config) {
|
|
|
6895
7173
|
} else if (config.physics !== "none") {
|
|
6896
7174
|
out.physics = config.physics;
|
|
6897
7175
|
}
|
|
6898
|
-
|
|
7176
|
+
const scene = diffAgainst(config.scene, sceneSchema.parse({}));
|
|
7177
|
+
if (Object.keys(scene).length > 0) out.scene = scene;
|
|
6899
7178
|
if (config.onTwos) out.onTwos = true;
|
|
6900
7179
|
if (config.states) out.states = config.states;
|
|
6901
7180
|
const meta = diffAgainst(config.meta, paperConfigSchema.parse({}).meta);
|
|
@@ -6906,14 +7185,35 @@ function jsxValue(value) {
|
|
|
6906
7185
|
if (typeof value === "string") return `"${value}"`;
|
|
6907
7186
|
return `{${JSON.stringify(value)}}`;
|
|
6908
7187
|
}
|
|
7188
|
+
function withoutUploads(value) {
|
|
7189
|
+
let replaced = 0;
|
|
7190
|
+
const walk = (node) => {
|
|
7191
|
+
if (typeof node === "string") {
|
|
7192
|
+
if (!node.startsWith("data:")) return node;
|
|
7193
|
+
replaced++;
|
|
7194
|
+
const extension = node.startsWith("data:image/png") ? "png" : "jpg";
|
|
7195
|
+
return `/paperlab-image-${replaced}.${extension}`;
|
|
7196
|
+
}
|
|
7197
|
+
if (Array.isArray(node)) return node.map(walk);
|
|
7198
|
+
if (node && typeof node === "object") {
|
|
7199
|
+
return Object.fromEntries(Object.entries(node).map(([k, v]) => [k, walk(v)]));
|
|
7200
|
+
}
|
|
7201
|
+
return node;
|
|
7202
|
+
};
|
|
7203
|
+
return { value: walk(value), replaced };
|
|
7204
|
+
}
|
|
7205
|
+
var UPLOAD_NOTE = "// Uploaded pictures cannot travel in a snippet \u2014 the paths below are\n// stand-ins, in order. Point them at your own files.";
|
|
6909
7206
|
function buildJsxSnippet(config) {
|
|
6910
|
-
const
|
|
6911
|
-
delete
|
|
7207
|
+
const diffed = diffConfig(config);
|
|
7208
|
+
delete diffed.meta;
|
|
7209
|
+
const { value: diff, replaced } = withoutUploads(diffed);
|
|
6912
7210
|
const props = Object.entries(diff).map(([key, value]) => ` ${key}=${jsxValue(value)}`);
|
|
6913
7211
|
if (props.length === 0) return "<Paper />";
|
|
6914
|
-
|
|
7212
|
+
const snippet = `<Paper
|
|
6915
7213
|
${props.join("\n")}
|
|
6916
7214
|
/>`;
|
|
7215
|
+
return replaced > 0 ? `${UPLOAD_NOTE}
|
|
7216
|
+
${snippet}` : snippet;
|
|
6917
7217
|
}
|
|
6918
7218
|
|
|
6919
7219
|
// src/config/agent-payload.ts
|
|
@@ -6963,7 +7263,10 @@ function componentName(config) {
|
|
|
6963
7263
|
}
|
|
6964
7264
|
function buildAgentPayload(config) {
|
|
6965
7265
|
const name = componentName(config);
|
|
6966
|
-
const
|
|
7266
|
+
const { value: stripped, replaced } = withoutUploads(diffConfig(config));
|
|
7267
|
+
const preset = JSON.stringify(stripped, null, 2);
|
|
7268
|
+
const uploadNote = replaced > 0 ? `${UPLOAD_NOTE}
|
|
7269
|
+
` : "";
|
|
6967
7270
|
return `Integrate a Paperlab paper component into this project. (paperlab agent-payload v${AGENT_PAYLOAD_VERSION})
|
|
6968
7271
|
|
|
6969
7272
|
1. Install the dependencies:
|
|
@@ -6976,7 +7279,7 @@ function buildAgentPayload(config) {
|
|
|
6976
7279
|
\`\`\`tsx
|
|
6977
7280
|
import { Paper, type PaperConfigInput } from 'paperlab'
|
|
6978
7281
|
|
|
6979
|
-
const preset = ${preset.replace(/\n/g, "\n")} satisfies PaperConfigInput
|
|
7282
|
+
${uploadNote}const preset = ${preset.replace(/\n/g, "\n")} satisfies PaperConfigInput
|
|
6980
7283
|
|
|
6981
7284
|
export function ${name}() {
|
|
6982
7285
|
return <Paper preset={preset} />
|
|
@@ -7019,7 +7322,9 @@ function distinctFieldPresets(input) {
|
|
|
7019
7322
|
function nonDefault(value, defaults) {
|
|
7020
7323
|
const out = {};
|
|
7021
7324
|
for (const [k, v] of Object.entries(value ?? {})) {
|
|
7022
|
-
if (v
|
|
7325
|
+
if (v === void 0) continue;
|
|
7326
|
+
const same = v === defaults[k] || JSON.stringify(v) === JSON.stringify(defaults[k]);
|
|
7327
|
+
if (!same) out[k] = v;
|
|
7023
7328
|
}
|
|
7024
7329
|
return out;
|
|
7025
7330
|
}
|
|
@@ -7032,6 +7337,11 @@ function diffFieldProps(input) {
|
|
|
7032
7337
|
if (Object.keys(motion).length > 0) out.motion = motion;
|
|
7033
7338
|
const entrance = nonDefault(input.entrance, ENTRANCE_DEFAULTS);
|
|
7034
7339
|
if (Object.keys(entrance).length > 0) out.entrance = entrance;
|
|
7340
|
+
const scene = nonDefault(
|
|
7341
|
+
sceneSchema.parse(input.scene ?? {}),
|
|
7342
|
+
sceneSchema.parse({})
|
|
7343
|
+
);
|
|
7344
|
+
if (Object.keys(scene).length > 0) out.scene = scene;
|
|
7035
7345
|
return out;
|
|
7036
7346
|
}
|
|
7037
7347
|
var LAYOUT_PHRASES = {
|
|
@@ -7065,7 +7375,7 @@ function describeFieldConfig(input) {
|
|
|
7065
7375
|
parts.push("hovering peels/reacts per paper (interaction states)");
|
|
7066
7376
|
if (zones.length > 0) {
|
|
7067
7377
|
parts.push(
|
|
7068
|
-
`dragging one past its tear threshold detaches it to carry to the ${zones.map((
|
|
7378
|
+
`dragging one past its tear threshold detaches it to carry to the ${zones.map((z25) => z25.id).join(" or ")} zone (release elsewhere flutters it back)`
|
|
7069
7379
|
);
|
|
7070
7380
|
}
|
|
7071
7381
|
} else {
|
|
@@ -7174,7 +7484,7 @@ required; the component needs no props.`;
|
|
|
7174
7484
|
}
|
|
7175
7485
|
|
|
7176
7486
|
// src/field/parity.ts
|
|
7177
|
-
var
|
|
7487
|
+
var THREE17 = __toESM(require("three"), 1);
|
|
7178
7488
|
var GRID = 48;
|
|
7179
7489
|
var PARITY_EPSILON = 5e-4;
|
|
7180
7490
|
var parityCases = [
|
|
@@ -7450,7 +7760,7 @@ function runParityHarness(canvas) {
|
|
|
7450
7760
|
if (!gl.getExtension("EXT_color_buffer_float")) {
|
|
7451
7761
|
throw new Error("[paperlab parity] EXT_color_buffer_float unavailable");
|
|
7452
7762
|
}
|
|
7453
|
-
const point = new
|
|
7763
|
+
const point = new THREE17.Vector3();
|
|
7454
7764
|
const parity = parityCases.map((c) => {
|
|
7455
7765
|
const gpu = runCaseOnGPU(gl, c);
|
|
7456
7766
|
let maxError = 0;
|
|
@@ -7481,17 +7791,21 @@ function runParityHarness(canvas) {
|
|
|
7481
7791
|
LightRig,
|
|
7482
7792
|
PARITY_EPSILON,
|
|
7483
7793
|
Paper,
|
|
7794
|
+
PaperBackdrop,
|
|
7484
7795
|
PaperField,
|
|
7485
7796
|
PaperFieldMesh,
|
|
7486
7797
|
PaperLighting,
|
|
7487
7798
|
PaperMesh,
|
|
7488
7799
|
SAG_TOL,
|
|
7800
|
+
backdropSchema,
|
|
7489
7801
|
behaviorConfigSchema,
|
|
7490
7802
|
buildAgentPayload,
|
|
7491
7803
|
buildFieldAgentPayload,
|
|
7492
7804
|
buildFieldComponentSource,
|
|
7493
7805
|
buildJsxSnippet,
|
|
7494
7806
|
clothConfigSchema,
|
|
7807
|
+
contentNames,
|
|
7808
|
+
contentSchemaFor,
|
|
7495
7809
|
coreStateNames,
|
|
7496
7810
|
describeConfig,
|
|
7497
7811
|
describeFieldConfig,
|
|
@@ -7528,6 +7842,7 @@ function runParityHarness(canvas) {
|
|
|
7528
7842
|
resolveLighting,
|
|
7529
7843
|
resolveStateConfig,
|
|
7530
7844
|
runParityHarness,
|
|
7845
|
+
sceneSchema,
|
|
7531
7846
|
segmentsForArc,
|
|
7532
7847
|
segmentsForSine,
|
|
7533
7848
|
serializePreset,
|
|
@@ -7541,6 +7856,7 @@ function runParityHarness(canvas) {
|
|
|
7541
7856
|
unregisterPreset,
|
|
7542
7857
|
usePaperStates,
|
|
7543
7858
|
usePrefersReducedMotion,
|
|
7859
|
+
washSchema,
|
|
7544
7860
|
wrapLines
|
|
7545
7861
|
});
|
|
7546
7862
|
//# sourceMappingURL=index.cjs.map
|