paperlab 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -16
- package/dist/{chunk-4ZU5DZEF.js → chunk-6IADJZX5.js} +508 -224
- package/dist/chunk-6IADJZX5.js.map +1 -0
- package/dist/index.cjs +571 -272
- 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 +23 -2
- 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 +2377 -2111
- 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 +29 -11
- package/dist/stage.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-4ZU5DZEF.js.map +0 -1
- package/dist/slots-BYoLxVZ4.d.cts +0 -6008
- package/dist/slots-BYoLxVZ4.d.ts +0 -6008
|
@@ -909,6 +909,31 @@ var stockNames = [
|
|
|
909
909
|
"sticker"
|
|
910
910
|
];
|
|
911
911
|
var stockSchema = z14.enum(stockNames);
|
|
912
|
+
var washSchema = z14.object({
|
|
913
|
+
/** The first pigment. */
|
|
914
|
+
color: z14.string().default("#4a5b8c").describe("color"),
|
|
915
|
+
/** The second. Blooms alternate, and overlaps multiply into a third. */
|
|
916
|
+
secondary: z14.string().default("#b06a6a").describe("color"),
|
|
917
|
+
/** How many pools of colour. */
|
|
918
|
+
blooms: z14.number().int().min(1).max(24).default(7),
|
|
919
|
+
/** How far a pool runs before it dries — its size against the sheet. */
|
|
920
|
+
spread: z14.number().min(0.1).max(1).default(0.7),
|
|
921
|
+
/** Softness of the wet edge. 0 is a hard cut, 1 is a pool still moving. */
|
|
922
|
+
bleed: z14.number().min(0).max(1).default(0.5),
|
|
923
|
+
/** How much pigment is in the water. */
|
|
924
|
+
intensity: z14.number().min(0).max(1).default(0.55),
|
|
925
|
+
/**
|
|
926
|
+
* Edge darkening — the ring of pigment left where a pool dried.
|
|
927
|
+
*
|
|
928
|
+
* The signature of the medium, and the one thing a plain gradient cannot
|
|
929
|
+
* fake. Without it a wash reads as an airbrush.
|
|
930
|
+
*/
|
|
931
|
+
edge: z14.number().min(0).max(1).default(0.6),
|
|
932
|
+
/** Pigment settling into the tooth of the paper. */
|
|
933
|
+
granulation: z14.number().min(0).max(1).default(0.35),
|
|
934
|
+
/** Fixed so a preset paints the same wash every time. */
|
|
935
|
+
seed: z14.number().int().min(0).max(99).default(0)
|
|
936
|
+
});
|
|
912
937
|
var blankContentBase = z14.object({
|
|
913
938
|
type: z14.literal("blank")
|
|
914
939
|
});
|
|
@@ -933,7 +958,7 @@ var textContentBase = z14.object({
|
|
|
933
958
|
/** px at texture resolution (long edge = 1024 logical px before DPR). */
|
|
934
959
|
size: z14.number().min(8).max(256).default(44),
|
|
935
960
|
weight: z14.number().min(100).max(900).default(400),
|
|
936
|
-
color: z14.string().default("#2b2620"),
|
|
961
|
+
color: z14.string().default("#2b2620").describe("color"),
|
|
937
962
|
align: z14.enum(["left", "center", "right"]).default("left"),
|
|
938
963
|
/** Fraction of the short edge. */
|
|
939
964
|
padding: z14.number().min(0).max(0.4).default(0.09),
|
|
@@ -983,7 +1008,7 @@ var cardContentBase = z14.object({
|
|
|
983
1008
|
* was cropped rather than as a card that was set.
|
|
984
1009
|
*/
|
|
985
1010
|
size: z14.number().min(8).max(256).default(58),
|
|
986
|
-
color: z14.string().default("#2b2620"),
|
|
1011
|
+
color: z14.string().default("#2b2620").describe("color"),
|
|
987
1012
|
align: z14.enum(["left", "center"]).default("left"),
|
|
988
1013
|
padding: z14.number().min(0).max(0.4).default(0.1)
|
|
989
1014
|
});
|
|
@@ -1002,14 +1027,15 @@ var receiptContentBase = z14.object({
|
|
|
1002
1027
|
timestamp: z14.string().optional(),
|
|
1003
1028
|
footer: z14.string().default("KEEP FOR YOUR RECORDS")
|
|
1004
1029
|
});
|
|
1030
|
+
var withWash = { wash: washSchema.optional() };
|
|
1005
1031
|
var backContentSchema = z14.discriminatedUnion("type", [
|
|
1006
|
-
blankContentBase,
|
|
1007
|
-
imageContentBase,
|
|
1008
|
-
textContentBase,
|
|
1009
|
-
cardContentBase,
|
|
1010
|
-
receiptContentBase
|
|
1032
|
+
blankContentBase.extend(withWash),
|
|
1033
|
+
imageContentBase.extend(withWash),
|
|
1034
|
+
textContentBase.extend(withWash),
|
|
1035
|
+
cardContentBase.extend(withWash),
|
|
1036
|
+
receiptContentBase.extend(withWash)
|
|
1011
1037
|
]);
|
|
1012
|
-
var withBack = { back: backContentSchema.optional() };
|
|
1038
|
+
var withBack = { back: backContentSchema.optional(), ...withWash };
|
|
1013
1039
|
var blankContentSchema = blankContentBase.extend(withBack);
|
|
1014
1040
|
var imageContentSchema = imageContentBase.extend(withBack);
|
|
1015
1041
|
var textContentSchema = textContentBase.extend(withBack);
|
|
@@ -1022,6 +1048,14 @@ var contentSchema = z14.discriminatedUnion("type", [
|
|
|
1022
1048
|
cardContentSchema,
|
|
1023
1049
|
receiptContentSchema
|
|
1024
1050
|
]);
|
|
1051
|
+
var contentNames = contentSchema.options.map(
|
|
1052
|
+
(option) => option.shape.type.value
|
|
1053
|
+
);
|
|
1054
|
+
function contentSchemaFor(type) {
|
|
1055
|
+
const option = contentSchema.options.find((candidate) => candidate.shape.type.value === type);
|
|
1056
|
+
if (!option) throw new Error(`Unknown content type: ${type}`);
|
|
1057
|
+
return option;
|
|
1058
|
+
}
|
|
1025
1059
|
var paperEdges = ["top", "right", "bottom", "left"];
|
|
1026
1060
|
var surfaceSchema = z14.object({
|
|
1027
1061
|
/** Paper fiber noise, 0..1. */
|
|
@@ -1109,8 +1143,70 @@ var lightingNames = [
|
|
|
1109
1143
|
"lightbox"
|
|
1110
1144
|
];
|
|
1111
1145
|
var filmNames = ["agx", "neutral", "filmic"];
|
|
1146
|
+
var lightSchema = z14.object({
|
|
1147
|
+
/** Tone-mapping exposure — the stop the whole picture is printed at. */
|
|
1148
|
+
exposure: z14.number().min(0.1).max(4).optional(),
|
|
1149
|
+
/**
|
|
1150
|
+
* The tone curve — the film, where `exposure` is the stop.
|
|
1151
|
+
*
|
|
1152
|
+
* `filmic` is ACES, which is what every preset used to be pinned to and is
|
|
1153
|
+
* kept so a scene tuned against it can say so. On near-white paper it is
|
|
1154
|
+
* the wrong film: it desaturates and drags bright neutrals toward
|
|
1155
|
+
* yellow-green, which is the sepia cast a lit sheet used to pick up.
|
|
1156
|
+
*/
|
|
1157
|
+
film: z14.enum(filmNames).optional(),
|
|
1158
|
+
/** Key light strength. */
|
|
1159
|
+
key: z14.number().min(0).max(12).optional(),
|
|
1160
|
+
/** Key light colour. */
|
|
1161
|
+
color: z14.string().optional().describe("color"),
|
|
1162
|
+
/**
|
|
1163
|
+
* Where the key stands, degrees around the vertical. 0° is straight in
|
|
1164
|
+
* front of the paper (+Z, beside the camera), 90° is off to the right,
|
|
1165
|
+
* and ±180° is directly behind it — which is where `nave` puts it, and
|
|
1166
|
+
* why that preset is carried by light coming THROUGH the paper.
|
|
1167
|
+
*/
|
|
1168
|
+
direction: z14.number().min(-180).max(180).optional(),
|
|
1169
|
+
/** How high the key stands, degrees above the horizon. */
|
|
1170
|
+
height: z14.number().min(-30).max(89).optional(),
|
|
1171
|
+
/** Flat fill from every direction at once. Cheap, and it kills form — reach for `studio` first. */
|
|
1172
|
+
ambient: z14.number().min(0).max(2).optional(),
|
|
1173
|
+
/** The room's own light: an environment map built from `sky`. Directional fill, and the only thing paper's sheen has to reflect. */
|
|
1174
|
+
studio: z14.number().min(0).max(3).optional(),
|
|
1175
|
+
/** Distance haze, as a multiple of the preset's. 0 clears the air entirely; 2 halves the distance you can see. */
|
|
1176
|
+
haze: z14.number().min(0).max(3).optional()
|
|
1177
|
+
});
|
|
1178
|
+
var backdropSchema = z14.object({
|
|
1179
|
+
/** Behind everything, and behind the picture where it does not reach. */
|
|
1180
|
+
color: z14.string().default("#171717").describe("color"),
|
|
1181
|
+
/** A URL, or an uploaded picture. Empty is the colour on its own. */
|
|
1182
|
+
image: z14.string().default(""),
|
|
1183
|
+
fit: z14.enum(["cover", "contain"]).default("cover"),
|
|
1184
|
+
/**
|
|
1185
|
+
* Toward the colour, so the paper stays the subject.
|
|
1186
|
+
*
|
|
1187
|
+
* A backdrop at full strength competes with the sheet in front of it —
|
|
1188
|
+
* which is what a real photographer solves by putting the background out
|
|
1189
|
+
* of the light, and what this solves by mixing it back toward the ground
|
|
1190
|
+
* it sits on.
|
|
1191
|
+
*/
|
|
1192
|
+
fade: z14.number().min(0).max(1).default(0.25),
|
|
1193
|
+
/** Out of focus, for the same reason. */
|
|
1194
|
+
blur: z14.number().min(0).max(1).default(0.2)
|
|
1195
|
+
});
|
|
1112
1196
|
var sceneSchema = z14.object({
|
|
1113
|
-
lighting: z14.enum(lightingNames).default("studio")
|
|
1197
|
+
lighting: z14.enum(lightingNames).default("studio"),
|
|
1198
|
+
/** What is behind the sheet. Unset leaves the canvas alone. */
|
|
1199
|
+
backdrop: backdropSchema.optional(),
|
|
1200
|
+
/**
|
|
1201
|
+
* Overrides on the named preset — the same authorable half stage mode has
|
|
1202
|
+
* always had, and which a lone sheet had no way to reach.
|
|
1203
|
+
*
|
|
1204
|
+
* A preset was the starting point everywhere EXCEPT here: a stage could be
|
|
1205
|
+
* "nave, but the sun is lower", and a `<Paper>` could only be one of seven
|
|
1206
|
+
* rigs exactly as shipped. `<PaperLighting>` has taken these overrides all
|
|
1207
|
+
* along; nothing was passing them.
|
|
1208
|
+
*/
|
|
1209
|
+
light: lightSchema.default({})
|
|
1114
1210
|
});
|
|
1115
1211
|
var coreStateNames = ["rest", "hover", "pressed", "picked", "placed"];
|
|
1116
1212
|
var isStateName = (s) => coreStateNames.includes(s) || s.startsWith("custom:");
|
|
@@ -1332,6 +1428,33 @@ var builtins = {
|
|
|
1332
1428
|
behavior: { type: "letter-fold", progress: 0.4, crease: 0.3 },
|
|
1333
1429
|
surface: { creaseLines: { angle: 0, positions: [1 / 3, 2 / 3], strength: 0.5 } }
|
|
1334
1430
|
},
|
|
1431
|
+
/**
|
|
1432
|
+
* The wash, shown rather than described.
|
|
1433
|
+
*
|
|
1434
|
+
* A `wash` is a field on every content type, which means it is reachable
|
|
1435
|
+
* from any preset and discoverable from none — a toggle three folders down
|
|
1436
|
+
* is not an argument for the feature. This is the argument: type set over
|
|
1437
|
+
* paint, on cotton, deckled, with the paper still showing through both.
|
|
1438
|
+
*/
|
|
1439
|
+
"washed-letter": {
|
|
1440
|
+
meta: { name: "Washed letter", tags: ["wash", "text"] },
|
|
1441
|
+
// Printer stock, not vellum: vellum is translucent and takes the room's
|
|
1442
|
+
// grey through the back of the sheet, which turns a wash the colour of
|
|
1443
|
+
// dishwater. Pigment needs something white behind it.
|
|
1444
|
+
sheet: { width: 1.05, height: 1.45 },
|
|
1445
|
+
stock: "printer",
|
|
1446
|
+
content: {
|
|
1447
|
+
type: "text",
|
|
1448
|
+
text: "Painted first,\nwritten after.",
|
|
1449
|
+
font: 'Georgia, "Times New Roman", serif',
|
|
1450
|
+
size: 52,
|
|
1451
|
+
valign: "center",
|
|
1452
|
+
align: "center",
|
|
1453
|
+
wash: { color: "#5b6f9a", secondary: "#c08a86", blooms: 6, spread: 0.85, intensity: 0.5, seed: 3 }
|
|
1454
|
+
},
|
|
1455
|
+
behavior: { type: "peel", progress: 0.12, corner: "bottom-right", radius: 0.3 },
|
|
1456
|
+
surface: { grain: 0.35, deckle: { edges: ["bottom", "right"], roughness: 0.45 } }
|
|
1457
|
+
},
|
|
1335
1458
|
"vintage-note": {
|
|
1336
1459
|
meta: { name: "Vintage note", tags: ["aging", "text"] },
|
|
1337
1460
|
sheet: { width: 1.1, height: 1.4 },
|
|
@@ -2266,7 +2389,6 @@ function getIdlePreset(name) {
|
|
|
2266
2389
|
}
|
|
2267
2390
|
|
|
2268
2391
|
// src/scene/lighting.ts
|
|
2269
|
-
import { z as z22 } from "zod";
|
|
2270
2392
|
var lightingPresets = {
|
|
2271
2393
|
studio: {
|
|
2272
2394
|
id: "studio",
|
|
@@ -2427,46 +2549,14 @@ var lightingPresets = {
|
|
|
2427
2549
|
function getLightingPreset(name) {
|
|
2428
2550
|
return lightingPresets[name];
|
|
2429
2551
|
}
|
|
2430
|
-
var lightSchema = z22.object({
|
|
2431
|
-
/** Tone-mapping exposure — the stop the whole picture is printed at. */
|
|
2432
|
-
exposure: z22.number().min(0.1).max(4).optional(),
|
|
2433
|
-
/**
|
|
2434
|
-
* The tone curve — the film, where `exposure` is the stop.
|
|
2435
|
-
*
|
|
2436
|
-
* `filmic` is ACES, which is what every preset used to be pinned to and is
|
|
2437
|
-
* kept so a scene tuned against it can say so. On near-white paper it is
|
|
2438
|
-
* the wrong film: it desaturates and drags bright neutrals toward
|
|
2439
|
-
* yellow-green, which is the sepia cast a lit sheet used to pick up.
|
|
2440
|
-
*/
|
|
2441
|
-
film: z22.enum(filmNames).optional(),
|
|
2442
|
-
/** Key light strength. */
|
|
2443
|
-
key: z22.number().min(0).max(12).optional(),
|
|
2444
|
-
/** Key light colour. */
|
|
2445
|
-
color: z22.string().optional(),
|
|
2446
|
-
/**
|
|
2447
|
-
* Where the key stands, degrees around the vertical. 0° is straight in
|
|
2448
|
-
* front of the paper (+Z, beside the camera), 90° is off to the right,
|
|
2449
|
-
* and ±180° is directly behind it — which is where `nave` puts it, and
|
|
2450
|
-
* why that preset is carried by light coming THROUGH the paper.
|
|
2451
|
-
*/
|
|
2452
|
-
direction: z22.number().min(-180).max(180).optional(),
|
|
2453
|
-
/** How high the key stands, degrees above the horizon. */
|
|
2454
|
-
height: z22.number().min(-30).max(89).optional(),
|
|
2455
|
-
/** Flat fill from every direction at once. Cheap, and it kills form — reach for `studio` first. */
|
|
2456
|
-
ambient: z22.number().min(0).max(2).optional(),
|
|
2457
|
-
/** The room's own light: an environment map built from `sky`. Directional fill, and the only thing paper's sheen has to reflect. */
|
|
2458
|
-
studio: z22.number().min(0).max(3).optional(),
|
|
2459
|
-
/** Distance haze, as a multiple of the preset's. 0 clears the air entirely; 2 halves the distance you can see. */
|
|
2460
|
-
haze: z22.number().min(0).max(3).optional()
|
|
2461
|
-
});
|
|
2462
2552
|
var DEG6 = 180 / Math.PI;
|
|
2463
2553
|
function lightAngles(position) {
|
|
2464
|
-
const [x, y,
|
|
2465
|
-
const distance = Math.hypot(x, y,
|
|
2554
|
+
const [x, y, z25] = position;
|
|
2555
|
+
const distance = Math.hypot(x, y, z25);
|
|
2466
2556
|
if (distance < 1e-9) return { azimuth: 0, elevation: 90, distance: 0 };
|
|
2467
|
-
const ground = Math.hypot(x,
|
|
2557
|
+
const ground = Math.hypot(x, z25);
|
|
2468
2558
|
return {
|
|
2469
|
-
azimuth: ground < 1e-9 ? 0 : Math.atan2(x,
|
|
2559
|
+
azimuth: ground < 1e-9 ? 0 : Math.atan2(x, z25) * DEG6,
|
|
2470
2560
|
elevation: Math.atan2(y, ground) * DEG6,
|
|
2471
2561
|
distance
|
|
2472
2562
|
};
|
|
@@ -3053,11 +3143,11 @@ function computeSheetNormals(geometry) {
|
|
|
3053
3143
|
for (let i = 0, l = nrm.length; i < l; i += 3) {
|
|
3054
3144
|
const x = nrm[i];
|
|
3055
3145
|
const y = nrm[i + 1];
|
|
3056
|
-
const
|
|
3057
|
-
const len = Math.sqrt(x * x + y * y +
|
|
3146
|
+
const z25 = nrm[i + 2];
|
|
3147
|
+
const len = Math.sqrt(x * x + y * y + z25 * z25) || 1;
|
|
3058
3148
|
nrm[i] = x / len;
|
|
3059
3149
|
nrm[i + 1] = y / len;
|
|
3060
|
-
nrm[i + 2] =
|
|
3150
|
+
nrm[i + 2] = z25 / len;
|
|
3061
3151
|
}
|
|
3062
3152
|
normalAttr.needsUpdate = true;
|
|
3063
3153
|
}
|
|
@@ -3190,6 +3280,109 @@ function paintCard(ctx, w, h, content, stock, dpr) {
|
|
|
3190
3280
|
}
|
|
3191
3281
|
}
|
|
3192
3282
|
|
|
3283
|
+
// src/content/wash.ts
|
|
3284
|
+
function mulberry32(seed) {
|
|
3285
|
+
let a = seed * 1831565813 + 2654435769;
|
|
3286
|
+
return () => {
|
|
3287
|
+
a |= 0;
|
|
3288
|
+
a = a + 1831565813 | 0;
|
|
3289
|
+
let t = Math.imul(a ^ a >>> 15, 1 | a);
|
|
3290
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
3291
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
3292
|
+
};
|
|
3293
|
+
}
|
|
3294
|
+
function rgba(hex, alpha) {
|
|
3295
|
+
const m = /^#?([0-9a-f]{6})$/i.exec(hex.trim());
|
|
3296
|
+
const n = m ? Number.parseInt(m[1], 16) : 8421504;
|
|
3297
|
+
return `rgba(${n >> 16 & 255}, ${n >> 8 & 255}, ${n & 255}, ${Math.max(0, Math.min(1, alpha))})`;
|
|
3298
|
+
}
|
|
3299
|
+
function bloomShape(rng) {
|
|
3300
|
+
return {
|
|
3301
|
+
phase: [rng() * Math.PI * 2, rng() * Math.PI * 2, rng() * Math.PI * 2],
|
|
3302
|
+
amp: [0.18 + rng() * 0.1, 0.09 + rng() * 0.06, 0.05 + rng() * 0.04]
|
|
3303
|
+
};
|
|
3304
|
+
}
|
|
3305
|
+
function bloomPath(ctx, cx, cy, radius, shape, from = 0, to = 1) {
|
|
3306
|
+
const { phase, amp } = shape;
|
|
3307
|
+
const STEPS = Math.max(6, Math.round(72 * (to - from)));
|
|
3308
|
+
ctx.beginPath();
|
|
3309
|
+
for (let i = 0; i <= STEPS; i++) {
|
|
3310
|
+
const t = (from + (to - from) * i / STEPS) * Math.PI * 2;
|
|
3311
|
+
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]);
|
|
3312
|
+
const r = radius * (1 + wobble);
|
|
3313
|
+
const x = cx + Math.cos(t) * r;
|
|
3314
|
+
const y = cy + Math.sin(t) * r;
|
|
3315
|
+
if (i === 0) ctx.moveTo(x, y);
|
|
3316
|
+
else ctx.lineTo(x, y);
|
|
3317
|
+
}
|
|
3318
|
+
if (from === 0 && to === 1) ctx.closePath();
|
|
3319
|
+
}
|
|
3320
|
+
function canBlur(ctx) {
|
|
3321
|
+
if (!("filter" in ctx)) return false;
|
|
3322
|
+
const before = ctx.filter;
|
|
3323
|
+
try {
|
|
3324
|
+
ctx.filter = "blur(2px)";
|
|
3325
|
+
const worked = ctx.filter !== "none" && ctx.filter !== before;
|
|
3326
|
+
ctx.filter = before;
|
|
3327
|
+
return worked;
|
|
3328
|
+
} catch {
|
|
3329
|
+
return false;
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
function paintWash(ctx, w, h, wash) {
|
|
3333
|
+
const rng = mulberry32(wash.seed + 1);
|
|
3334
|
+
const short = Math.min(w, h);
|
|
3335
|
+
const blurs = canBlur(ctx);
|
|
3336
|
+
ctx.save();
|
|
3337
|
+
ctx.globalCompositeOperation = "multiply";
|
|
3338
|
+
for (let i = 0; i < wash.blooms; i++) {
|
|
3339
|
+
const pigment = i % 2 === 0 ? wash.color : wash.secondary;
|
|
3340
|
+
const cx = (rng() * 1.5 - 0.25) * w;
|
|
3341
|
+
const cy = (rng() * 1.5 - 0.25) * h;
|
|
3342
|
+
const radius = short * wash.spread * (0.28 + rng() * 0.34);
|
|
3343
|
+
const alpha = wash.intensity * (0.35 + rng() * 0.4);
|
|
3344
|
+
const shape = bloomShape(rng);
|
|
3345
|
+
ctx.save();
|
|
3346
|
+
if (blurs) ctx.filter = `blur(${(2 + wash.bleed * 26) * (0.6 + rng() * 0.8)}px)`;
|
|
3347
|
+
const gx = cx + (rng() - 0.5) * radius * 0.5;
|
|
3348
|
+
const gy = cy + (rng() - 0.5) * radius * 0.5;
|
|
3349
|
+
const grad = ctx.createRadialGradient(gx, gy, radius * 0.05, gx, gy, radius * 1.15);
|
|
3350
|
+
grad.addColorStop(0, rgba(pigment, alpha * 0.55));
|
|
3351
|
+
grad.addColorStop(0.62, rgba(pigment, alpha));
|
|
3352
|
+
grad.addColorStop(1, rgba(pigment, alpha * 0.75));
|
|
3353
|
+
ctx.fillStyle = grad;
|
|
3354
|
+
bloomPath(ctx, cx, cy, radius, shape);
|
|
3355
|
+
ctx.fill();
|
|
3356
|
+
if (wash.edge > 0) {
|
|
3357
|
+
if (blurs) ctx.filter = `blur(${(1.5 + wash.bleed * 9) * (0.6 + rng() * 0.6)}px)`;
|
|
3358
|
+
ctx.lineWidth = Math.max(1, radius * (0.03 + wash.edge * 0.05));
|
|
3359
|
+
const arcs = 5;
|
|
3360
|
+
const overlap = 0.4 / arcs;
|
|
3361
|
+
for (let a = 0; a < arcs; a++) {
|
|
3362
|
+
ctx.strokeStyle = rgba(pigment, alpha * wash.edge * (0.15 + rng() * 1.05));
|
|
3363
|
+
bloomPath(ctx, cx, cy, radius, shape, a / arcs, (a + 1) / arcs + overlap);
|
|
3364
|
+
ctx.stroke();
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
ctx.restore();
|
|
3368
|
+
if (wash.granulation > 0) {
|
|
3369
|
+
ctx.save();
|
|
3370
|
+
bloomPath(ctx, cx, cy, radius, shape);
|
|
3371
|
+
ctx.clip();
|
|
3372
|
+
const grains = Math.round(wash.granulation * 900);
|
|
3373
|
+
ctx.fillStyle = rgba(pigment, wash.granulation * alpha * 0.5);
|
|
3374
|
+
for (let g = 0; g < grains; g++) {
|
|
3375
|
+
const a = rng() * Math.PI * 2;
|
|
3376
|
+
const d = Math.sqrt(rng()) * radius;
|
|
3377
|
+
const size = 1 + rng() * (short * 4e-3);
|
|
3378
|
+
ctx.fillRect(cx + Math.cos(a) * d, cy + Math.sin(a) * d, size, size);
|
|
3379
|
+
}
|
|
3380
|
+
ctx.restore();
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
ctx.restore();
|
|
3384
|
+
}
|
|
3385
|
+
|
|
3193
3386
|
// src/content/texture.ts
|
|
3194
3387
|
var LONG_EDGE = 1024;
|
|
3195
3388
|
var DPR = 2;
|
|
@@ -3240,6 +3433,7 @@ function renderContentToCanvas(content, sheet2, stock, image) {
|
|
|
3240
3433
|
canvas.height = h;
|
|
3241
3434
|
const ctx = canvas.getContext("2d");
|
|
3242
3435
|
paintBackground(ctx, w, h, stock);
|
|
3436
|
+
if (content.wash) paintWash(ctx, w, h, content.wash);
|
|
3243
3437
|
if (content.type === "image" && image && content.src) paintImage(ctx, w, h, image, content.fit);
|
|
3244
3438
|
if (content.type === "text") paintText(ctx, w, h, content, stock);
|
|
3245
3439
|
if (content.type === "receipt") paintReceipt(ctx, w, h, content, stock);
|
|
@@ -3451,13 +3645,13 @@ var ClothSim = class {
|
|
|
3451
3645
|
this.stillFrames = 0;
|
|
3452
3646
|
}
|
|
3453
3647
|
/** Nearest particle to a local-space point — the grab interface. */
|
|
3454
|
-
grabNearest(x, y,
|
|
3648
|
+
grabNearest(x, y, z25) {
|
|
3455
3649
|
let best = -1;
|
|
3456
3650
|
let bestDist = Infinity;
|
|
3457
3651
|
for (let i = 0; i < this.count; i++) {
|
|
3458
3652
|
const dx = this.positions[i * 3] - x;
|
|
3459
3653
|
const dy = this.positions[i * 3 + 1] - y;
|
|
3460
|
-
const dz = this.positions[i * 3 + 2] -
|
|
3654
|
+
const dz = this.positions[i * 3 + 2] - z25;
|
|
3461
3655
|
const d = dx * dx + dy * dy + dz * dz;
|
|
3462
3656
|
if (d < bestDist) {
|
|
3463
3657
|
bestDist = d;
|
|
@@ -3468,15 +3662,15 @@ var ClothSim = class {
|
|
|
3468
3662
|
this.wake();
|
|
3469
3663
|
return best;
|
|
3470
3664
|
}
|
|
3471
|
-
moveGrab(x, y,
|
|
3665
|
+
moveGrab(x, y, z25) {
|
|
3472
3666
|
if (this.grabbedIndex < 0) return;
|
|
3473
3667
|
const i3 = this.grabbedIndex * 3;
|
|
3474
3668
|
this.positions[i3] = x;
|
|
3475
3669
|
this.positions[i3 + 1] = y;
|
|
3476
|
-
this.positions[i3 + 2] =
|
|
3670
|
+
this.positions[i3 + 2] = z25;
|
|
3477
3671
|
this.prev[i3] = x;
|
|
3478
3672
|
this.prev[i3 + 1] = y;
|
|
3479
|
-
this.prev[i3 + 2] =
|
|
3673
|
+
this.prev[i3 + 2] = z25;
|
|
3480
3674
|
this.wake();
|
|
3481
3675
|
}
|
|
3482
3676
|
release() {
|
|
@@ -3512,19 +3706,19 @@ var ClothSim = class {
|
|
|
3512
3706
|
}
|
|
3513
3707
|
const x = p[i3];
|
|
3514
3708
|
const y = p[i3 + 1];
|
|
3515
|
-
const
|
|
3709
|
+
const z25 = p[i3 + 2];
|
|
3516
3710
|
const gust2 = wind * (0.55 + 0.45 * Math.sin(this.time * 1.7 + x * 2.1 + y * 1.3)) * 0.9;
|
|
3517
3711
|
const ax = gust2 * 0.25;
|
|
3518
3712
|
const az = gust2;
|
|
3519
3713
|
const vx = (x - this.prev[i3]) * damping;
|
|
3520
3714
|
const vy = (y - this.prev[i3 + 1]) * damping;
|
|
3521
|
-
const vz = (
|
|
3715
|
+
const vz = (z25 - this.prev[i3 + 2]) * damping;
|
|
3522
3716
|
this.prev[i3] = x;
|
|
3523
3717
|
this.prev[i3 + 1] = y;
|
|
3524
|
-
this.prev[i3 + 2] =
|
|
3718
|
+
this.prev[i3 + 2] = z25;
|
|
3525
3719
|
p[i3] = x + vx + ax * dt2;
|
|
3526
3720
|
p[i3 + 1] = y + vy - gravity * 3.2 * dt2;
|
|
3527
|
-
p[i3 + 2] =
|
|
3721
|
+
p[i3 + 2] = z25 + vz + az * dt2;
|
|
3528
3722
|
maxTravel = Math.max(maxTravel, vx * vx + vy * vy + vz * vz);
|
|
3529
3723
|
}
|
|
3530
3724
|
for (let iter = 0; iter < SOLVER_ITERATIONS; iter++) {
|
|
@@ -3632,8 +3826,8 @@ vec3 plTransmission(vec3 inkFilter) {
|
|
|
3632
3826
|
);
|
|
3633
3827
|
function translucencyValues(translucency, lighting) {
|
|
3634
3828
|
const preset = typeof lighting === "string" ? getLightingPreset(lighting) : lighting;
|
|
3635
|
-
const [x, y,
|
|
3636
|
-
const direction = new THREE4.Vector3(x, y,
|
|
3829
|
+
const [x, y, z25] = preset.key.position;
|
|
3830
|
+
const direction = new THREE4.Vector3(x, y, z25);
|
|
3637
3831
|
if (direction.lengthSq() < 1e-12) direction.set(0, 1, 0);
|
|
3638
3832
|
direction.normalize();
|
|
3639
3833
|
const color = new THREE4.Color(preset.key.color).multiplyScalar(preset.key.intensity * TRANSMISSION_GAIN);
|
|
@@ -4386,6 +4580,7 @@ var PaperMesh = forwardRef(function PaperMesh2(props, ref) {
|
|
|
4386
4580
|
props.interactive && !isCloth && behavior?.handles?.map((h, i) => /* @__PURE__ */ jsxs(
|
|
4387
4581
|
"mesh",
|
|
4388
4582
|
{
|
|
4583
|
+
userData: { paperlabChrome: true },
|
|
4389
4584
|
ref: (m) => {
|
|
4390
4585
|
handleRefs.current[i] = m;
|
|
4391
4586
|
},
|
|
@@ -4529,7 +4724,7 @@ var toneMappings = {
|
|
|
4529
4724
|
neutral: THREE9.NeutralToneMapping,
|
|
4530
4725
|
filmic: THREE9.ACESFilmicToneMapping
|
|
4531
4726
|
};
|
|
4532
|
-
function
|
|
4727
|
+
function mulberry322(seed) {
|
|
4533
4728
|
let a = seed;
|
|
4534
4729
|
return () => {
|
|
4535
4730
|
a |= 0;
|
|
@@ -4563,7 +4758,7 @@ function makeGoboTexture(kind) {
|
|
|
4563
4758
|
}
|
|
4564
4759
|
ctx.restore();
|
|
4565
4760
|
} else {
|
|
4566
|
-
const rand =
|
|
4761
|
+
const rand = mulberry322(7);
|
|
4567
4762
|
for (const [count, radius, alpha] of [
|
|
4568
4763
|
[26, 70, 0.75],
|
|
4569
4764
|
[40, 38, 0.6],
|
|
@@ -4711,9 +4906,75 @@ function PaperLighting({
|
|
|
4711
4906
|
] });
|
|
4712
4907
|
}
|
|
4713
4908
|
|
|
4714
|
-
// src/
|
|
4909
|
+
// src/scene/backdrop.tsx
|
|
4715
4910
|
import * as THREE10 from "three";
|
|
4716
|
-
import {
|
|
4911
|
+
import { useEffect as useEffect7, useState as useState4 } from "react";
|
|
4912
|
+
import { useThree as useThree3 } from "@react-three/fiber";
|
|
4913
|
+
function PaperBackdrop({ backdrop }) {
|
|
4914
|
+
const scene = useThree3((s) => s.scene);
|
|
4915
|
+
const size = useThree3((s) => s.size);
|
|
4916
|
+
const [image, setImage] = useState4(null);
|
|
4917
|
+
const src = backdrop?.image ?? "";
|
|
4918
|
+
useEffect7(() => {
|
|
4919
|
+
if (!src) {
|
|
4920
|
+
setImage(null);
|
|
4921
|
+
return;
|
|
4922
|
+
}
|
|
4923
|
+
let live = true;
|
|
4924
|
+
const img = new Image();
|
|
4925
|
+
img.crossOrigin = "anonymous";
|
|
4926
|
+
img.onload = () => live && setImage(img);
|
|
4927
|
+
img.onerror = () => live && setImage(null);
|
|
4928
|
+
img.src = src;
|
|
4929
|
+
return () => {
|
|
4930
|
+
live = false;
|
|
4931
|
+
};
|
|
4932
|
+
}, [src]);
|
|
4933
|
+
const key = JSON.stringify({ backdrop: backdrop ?? null, w: size.width, h: size.height, loaded: !!image });
|
|
4934
|
+
useEffect7(() => {
|
|
4935
|
+
if (!backdrop) return;
|
|
4936
|
+
const previous = scene.background;
|
|
4937
|
+
const texture = paintBackdrop(backdrop, size.width, size.height, image);
|
|
4938
|
+
scene.background = texture;
|
|
4939
|
+
return () => {
|
|
4940
|
+
scene.background = previous;
|
|
4941
|
+
texture.dispose();
|
|
4942
|
+
};
|
|
4943
|
+
}, [key]);
|
|
4944
|
+
return null;
|
|
4945
|
+
}
|
|
4946
|
+
function paintBackdrop(backdrop, width, height, image) {
|
|
4947
|
+
const w = Math.max(2, Math.round(width / 2));
|
|
4948
|
+
const h = Math.max(2, Math.round(height / 2));
|
|
4949
|
+
const canvas = document.createElement("canvas");
|
|
4950
|
+
canvas.width = w;
|
|
4951
|
+
canvas.height = h;
|
|
4952
|
+
const ctx = canvas.getContext("2d");
|
|
4953
|
+
ctx.fillStyle = backdrop.color;
|
|
4954
|
+
ctx.fillRect(0, 0, w, h);
|
|
4955
|
+
if (image && image.width > 0 && image.height > 0) {
|
|
4956
|
+
const scale = backdrop.fit === "cover" ? Math.max(w / image.width, h / image.height) : Math.min(w / image.width, h / image.height);
|
|
4957
|
+
const dw = image.width * scale;
|
|
4958
|
+
const dh = image.height * scale;
|
|
4959
|
+
ctx.save();
|
|
4960
|
+
if (backdrop.blur > 0) ctx.filter = `blur(${backdrop.blur * Math.min(w, h) * 0.06}px)`;
|
|
4961
|
+
ctx.drawImage(image, (w - dw) / 2, (h - dh) / 2, dw, dh);
|
|
4962
|
+
ctx.restore();
|
|
4963
|
+
if (backdrop.fade > 0) {
|
|
4964
|
+
ctx.globalAlpha = backdrop.fade;
|
|
4965
|
+
ctx.fillStyle = backdrop.color;
|
|
4966
|
+
ctx.fillRect(0, 0, w, h);
|
|
4967
|
+
ctx.globalAlpha = 1;
|
|
4968
|
+
}
|
|
4969
|
+
}
|
|
4970
|
+
const texture = new THREE10.CanvasTexture(canvas);
|
|
4971
|
+
texture.colorSpace = THREE10.SRGBColorSpace;
|
|
4972
|
+
return texture;
|
|
4973
|
+
}
|
|
4974
|
+
|
|
4975
|
+
// src/field/dropZones.tsx
|
|
4976
|
+
import * as THREE11 from "three";
|
|
4977
|
+
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect8, useMemo as useMemo5, useRef as useRef5, useSyncExternalStore } from "react";
|
|
4717
4978
|
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
4718
4979
|
var DropZoneRegistry = class {
|
|
4719
4980
|
zones = /* @__PURE__ */ new Map();
|
|
@@ -4771,10 +5032,10 @@ function DropZone(props) {
|
|
|
4771
5032
|
const registry4 = useContext2(DropZoneContext);
|
|
4772
5033
|
const { id, accept, bounds, highlight = "glow", onPlace } = props;
|
|
4773
5034
|
const place = useRef5(onPlace);
|
|
4774
|
-
|
|
5035
|
+
useEffect8(() => {
|
|
4775
5036
|
place.current = onPlace;
|
|
4776
5037
|
}, [onPlace]);
|
|
4777
|
-
|
|
5038
|
+
useEffect8(() => {
|
|
4778
5039
|
if (!registry4) return;
|
|
4779
5040
|
return registry4.register({
|
|
4780
5041
|
id,
|
|
@@ -4793,12 +5054,12 @@ function DropZoneVisual({ registry: registry4, config }) {
|
|
|
4793
5054
|
const style = config.highlight ?? "glow";
|
|
4794
5055
|
const [w, h] = config.bounds.size;
|
|
4795
5056
|
const edges = useMemo5(() => {
|
|
4796
|
-
const plane = new
|
|
4797
|
-
const geo = new
|
|
5057
|
+
const plane = new THREE11.PlaneGeometry(w, h);
|
|
5058
|
+
const geo = new THREE11.EdgesGeometry(plane);
|
|
4798
5059
|
plane.dispose();
|
|
4799
5060
|
return geo;
|
|
4800
5061
|
}, [w, h]);
|
|
4801
|
-
|
|
5062
|
+
useEffect8(() => () => edges.dispose(), [edges]);
|
|
4802
5063
|
return /* @__PURE__ */ jsxs3("group", { position: config.bounds.position, children: [
|
|
4803
5064
|
style !== "outline" && /* @__PURE__ */ jsxs3("mesh", { children: [
|
|
4804
5065
|
/* @__PURE__ */ jsx6("planeGeometry", { args: [w, h] }),
|
|
@@ -4817,18 +5078,18 @@ function DropZoneVisual({ registry: registry4, config }) {
|
|
|
4817
5078
|
}
|
|
4818
5079
|
|
|
4819
5080
|
// src/field/sheetGrid.ts
|
|
4820
|
-
import { z as
|
|
4821
|
-
var sheetLayoutSchema =
|
|
4822
|
-
rows:
|
|
4823
|
-
columns:
|
|
5081
|
+
import { z as z22 } from "zod";
|
|
5082
|
+
var sheetLayoutSchema = z22.object({
|
|
5083
|
+
rows: z22.number().int().min(1).max(12).default(2),
|
|
5084
|
+
columns: z22.number().int().min(1).max(12).default(5),
|
|
4824
5085
|
/** World-units gap between slots. Stamps are printed in register — no jitter. */
|
|
4825
|
-
gutter:
|
|
5086
|
+
gutter: z22.number().min(0).max(1).default(0.08),
|
|
4826
5087
|
/** Slot footprint in world units (the paper preset should match). */
|
|
4827
|
-
cellWidth:
|
|
4828
|
-
cellHeight:
|
|
5088
|
+
cellWidth: z22.number().min(0.1).max(4).default(0.72),
|
|
5089
|
+
cellHeight: z22.number().min(0.1).max(4).default(0.86),
|
|
4829
5090
|
/** Render the shared backing sheet behind the grid. */
|
|
4830
|
-
backing:
|
|
4831
|
-
backingMargin:
|
|
5091
|
+
backing: z22.boolean().default(true),
|
|
5092
|
+
backingMargin: z22.number().min(0).max(1).default(0.12)
|
|
4832
5093
|
});
|
|
4833
5094
|
var SHEET_LIFT = 0.012;
|
|
4834
5095
|
function withSheetCellFromPaper(parsed, rawOptions, paperDims) {
|
|
@@ -4875,18 +5136,18 @@ function tornEdgesOnDetach(i, o) {
|
|
|
4875
5136
|
}
|
|
4876
5137
|
|
|
4877
5138
|
// src/stage/path.ts
|
|
4878
|
-
import { z as
|
|
4879
|
-
var walkPathSchema =
|
|
5139
|
+
import { z as z23 } from "zod";
|
|
5140
|
+
var walkPathSchema = z23.object({
|
|
4880
5141
|
/**
|
|
4881
5142
|
* Control points on the ground plane, [x, z]. The default walks away from
|
|
4882
5143
|
* the camera down -Z — the shot every reference image is composed on.
|
|
4883
5144
|
*/
|
|
4884
|
-
points:
|
|
5145
|
+
points: z23.array(z23.tuple([z23.number(), z23.number()])).min(2).default([
|
|
4885
5146
|
[0, 9],
|
|
4886
5147
|
[0, -9]
|
|
4887
5148
|
]),
|
|
4888
5149
|
/** Join the last point back to the first: an endless walk, and the only form `phase` can slide. */
|
|
4889
|
-
closed:
|
|
5150
|
+
closed: z23.boolean().default(false)
|
|
4890
5151
|
});
|
|
4891
5152
|
var SAMPLES_PER_SEGMENT = 24;
|
|
4892
5153
|
var EPSILON = 1e-6;
|
|
@@ -5001,7 +5262,7 @@ function getWalkPath(options) {
|
|
|
5001
5262
|
}
|
|
5002
5263
|
|
|
5003
5264
|
// src/field/layouts/index.ts
|
|
5004
|
-
import { z as
|
|
5265
|
+
import { z as z24 } from "zod";
|
|
5005
5266
|
var DEFAULT_SHEET = { width: 1, height: 1.4 };
|
|
5006
5267
|
var TAU3 = Math.PI * 2;
|
|
5007
5268
|
var DEG7 = Math.PI / 180;
|
|
@@ -5013,9 +5274,9 @@ function jitter2(seed, i) {
|
|
|
5013
5274
|
function ramp(i, n) {
|
|
5014
5275
|
return n > 1 ? i / (n - 1) : 1;
|
|
5015
5276
|
}
|
|
5016
|
-
var ringSchema =
|
|
5017
|
-
radius:
|
|
5018
|
-
tiltDeg:
|
|
5277
|
+
var ringSchema = z24.object({
|
|
5278
|
+
radius: z24.number().min(0.5).max(12).default(2.6),
|
|
5279
|
+
tiltDeg: z24.number().min(-45).max(45).default(8)
|
|
5019
5280
|
});
|
|
5020
5281
|
var ring = {
|
|
5021
5282
|
id: "ring",
|
|
@@ -5033,15 +5294,15 @@ var ring = {
|
|
|
5033
5294
|
};
|
|
5034
5295
|
}
|
|
5035
5296
|
};
|
|
5036
|
-
var fanSchema =
|
|
5297
|
+
var fanSchema = z24.object({
|
|
5037
5298
|
/** Total angular sweep from the first sheet to the last, degrees. */
|
|
5038
|
-
sweep:
|
|
5299
|
+
sweep: z24.number().min(0).max(180).default(72),
|
|
5039
5300
|
/** Where the shared pin sits, in half-sheet-heights below center. 1 = the bottom edge. */
|
|
5040
|
-
hinge:
|
|
5301
|
+
hinge: z24.number().min(0).max(4).default(1.15),
|
|
5041
5302
|
/** Thickness step so the sheets stack in order instead of z-fighting. */
|
|
5042
|
-
lift:
|
|
5303
|
+
lift: z24.number().min(2e-3).max(0.08).default(0.012),
|
|
5043
5304
|
/** How much flatter the middle of the fan sits than its outer sheets. */
|
|
5044
|
-
bow:
|
|
5305
|
+
bow: z24.number().min(0).max(1).default(0.7)
|
|
5045
5306
|
});
|
|
5046
5307
|
var fan = {
|
|
5047
5308
|
id: "fan",
|
|
@@ -5061,16 +5322,16 @@ var fan = {
|
|
|
5061
5322
|
};
|
|
5062
5323
|
}
|
|
5063
5324
|
};
|
|
5064
|
-
var spreadSchema =
|
|
5325
|
+
var spreadSchema = z24.object({
|
|
5065
5326
|
/** How far each sheet slides past the one below it. */
|
|
5066
|
-
slip:
|
|
5327
|
+
slip: z24.number().min(0.02).max(2).default(0.3),
|
|
5067
5328
|
/** Direction of the slide, degrees. 0 slides right, 90 slides up. */
|
|
5068
|
-
angle:
|
|
5069
|
-
lift:
|
|
5329
|
+
angle: z24.number().min(-180).max(180).default(28),
|
|
5330
|
+
lift: z24.number().min(2e-3).max(0.08).default(0.012),
|
|
5070
5331
|
/** How much more the sheets at the far end of the slide bow. */
|
|
5071
|
-
bow:
|
|
5332
|
+
bow: z24.number().min(0).max(1).default(0.6),
|
|
5072
5333
|
/** Nothing hand-slid is perfectly square — a touch of per-sheet rotation. */
|
|
5073
|
-
drift:
|
|
5334
|
+
drift: z24.number().min(0).max(1).default(0.15)
|
|
5074
5335
|
});
|
|
5075
5336
|
var spread = {
|
|
5076
5337
|
id: "spread",
|
|
@@ -5088,15 +5349,15 @@ var spread = {
|
|
|
5088
5349
|
};
|
|
5089
5350
|
}
|
|
5090
5351
|
};
|
|
5091
|
-
var pileSchema =
|
|
5352
|
+
var pileSchema = z24.object({
|
|
5092
5353
|
/** How far sheets wander from the center of the heap. */
|
|
5093
|
-
scatter:
|
|
5354
|
+
scatter: z24.number().min(0).max(2).default(0.22),
|
|
5094
5355
|
/** Widest angle a sheet sits off square, degrees. */
|
|
5095
|
-
turn:
|
|
5096
|
-
lift:
|
|
5356
|
+
turn: z24.number().min(0).max(180).default(24),
|
|
5357
|
+
lift: z24.number().min(2e-3).max(0.08).default(0.011),
|
|
5097
5358
|
/** How flat the sheets underneath are pressed by the ones on top. */
|
|
5098
|
-
press:
|
|
5099
|
-
seed:
|
|
5359
|
+
press: z24.number().min(0).max(1).default(0.85),
|
|
5360
|
+
seed: z24.number().int().min(0).max(9999).default(3)
|
|
5100
5361
|
});
|
|
5101
5362
|
var pile = {
|
|
5102
5363
|
id: "pile",
|
|
@@ -5112,12 +5373,12 @@ var pile = {
|
|
|
5112
5373
|
};
|
|
5113
5374
|
}
|
|
5114
5375
|
};
|
|
5115
|
-
var wallSchema =
|
|
5116
|
-
gapX:
|
|
5117
|
-
gapY:
|
|
5118
|
-
jitterAmt:
|
|
5376
|
+
var wallSchema = z24.object({
|
|
5377
|
+
gapX: z24.number().min(0.05).max(1).default(0.22),
|
|
5378
|
+
gapY: z24.number().min(0.05).max(1).default(0.3),
|
|
5379
|
+
jitterAmt: z24.number().min(0).max(1).default(0.25),
|
|
5119
5380
|
/** Spread of sag across the wall — no two pinned sheets hang alike. */
|
|
5120
|
-
sag:
|
|
5381
|
+
sag: z24.number().min(0).max(1).default(0.45)
|
|
5121
5382
|
});
|
|
5122
5383
|
var wall = {
|
|
5123
5384
|
id: "wall",
|
|
@@ -5143,15 +5404,15 @@ var wall = {
|
|
|
5143
5404
|
};
|
|
5144
5405
|
}
|
|
5145
5406
|
};
|
|
5146
|
-
var spillSchema =
|
|
5147
|
-
spreadX:
|
|
5148
|
-
spreadY:
|
|
5149
|
-
depth:
|
|
5407
|
+
var spillSchema = z24.object({
|
|
5408
|
+
spreadX: z24.number().min(0.5).max(8).default(2.4),
|
|
5409
|
+
spreadY: z24.number().min(0.5).max(8).default(1.5),
|
|
5410
|
+
depth: z24.number().min(0).max(6).default(1.6),
|
|
5150
5411
|
/** How far sheets pitch and roll out of the picture plane. */
|
|
5151
|
-
tumble:
|
|
5412
|
+
tumble: z24.number().min(0).max(1).default(0.5),
|
|
5152
5413
|
/** Spread of bend across the sheets — a spill does not fold them alike. */
|
|
5153
|
-
vary:
|
|
5154
|
-
seed:
|
|
5414
|
+
vary: z24.number().min(0).max(1).default(0.6),
|
|
5415
|
+
seed: z24.number().int().min(0).max(9999).default(7)
|
|
5155
5416
|
});
|
|
5156
5417
|
var spill = {
|
|
5157
5418
|
id: "spill",
|
|
@@ -5176,13 +5437,13 @@ var spill = {
|
|
|
5176
5437
|
};
|
|
5177
5438
|
}
|
|
5178
5439
|
};
|
|
5179
|
-
var sweepSchema =
|
|
5180
|
-
columns:
|
|
5440
|
+
var sweepSchema = z24.object({
|
|
5441
|
+
columns: z24.number().int().min(1).max(24).default(5),
|
|
5181
5442
|
/** Breathing room around each specimen. */
|
|
5182
|
-
gap:
|
|
5443
|
+
gap: z24.number().min(0).max(2).default(0.22),
|
|
5183
5444
|
/** Deformation at the first specimen and at the last. */
|
|
5184
|
-
from:
|
|
5185
|
-
to:
|
|
5445
|
+
from: z24.number().min(0).max(1).default(0),
|
|
5446
|
+
to: z24.number().min(0).max(1).default(1)
|
|
5186
5447
|
});
|
|
5187
5448
|
var sweep = {
|
|
5188
5449
|
id: "sweep",
|
|
@@ -5206,15 +5467,15 @@ var sweep = {
|
|
|
5206
5467
|
};
|
|
5207
5468
|
}
|
|
5208
5469
|
};
|
|
5209
|
-
var bookSchema =
|
|
5470
|
+
var bookSchema = z24.object({
|
|
5210
5471
|
/** How far the outermost page lifts off the block, degrees. */
|
|
5211
|
-
spread:
|
|
5472
|
+
spread: z24.number().min(0).max(150).default(55),
|
|
5212
5473
|
/** Fraction of the pages bound to the left. 0 = a one-sided sample book. */
|
|
5213
|
-
split:
|
|
5474
|
+
split: z24.number().min(0).max(1).default(0.5),
|
|
5214
5475
|
/** Page thickness — the gap between pages of one block. */
|
|
5215
|
-
lift:
|
|
5476
|
+
lift: z24.number().min(1e-3).max(0.05).default(8e-3),
|
|
5216
5477
|
/** How much more a lifted page arcs than one lying flat in the block. */
|
|
5217
|
-
gutter:
|
|
5478
|
+
gutter: z24.number().min(0).max(1).default(0.6)
|
|
5218
5479
|
});
|
|
5219
5480
|
var book = {
|
|
5220
5481
|
id: "book",
|
|
@@ -5241,11 +5502,11 @@ var book = {
|
|
|
5241
5502
|
};
|
|
5242
5503
|
}
|
|
5243
5504
|
};
|
|
5244
|
-
var accordionSchema =
|
|
5505
|
+
var accordionSchema = z24.object({
|
|
5245
5506
|
/** How far each panel tilts off the strip's line, degrees. 0 = flat, 90 = shut. */
|
|
5246
|
-
angle:
|
|
5507
|
+
angle: z24.number().min(0).max(89).default(55),
|
|
5247
5508
|
/** A concertina holds its creases — how much bow the panels keep. */
|
|
5248
|
-
slack:
|
|
5509
|
+
slack: z24.number().min(0).max(1).default(0.15)
|
|
5249
5510
|
});
|
|
5250
5511
|
var accordion = {
|
|
5251
5512
|
id: "accordion",
|
|
@@ -5264,16 +5525,16 @@ var accordion = {
|
|
|
5264
5525
|
};
|
|
5265
5526
|
}
|
|
5266
5527
|
};
|
|
5267
|
-
var rackSchema =
|
|
5528
|
+
var rackSchema = z24.object({
|
|
5268
5529
|
/** Gap along the row, as a fraction of the paper's width. Under 1 they overlap. */
|
|
5269
|
-
spacing:
|
|
5530
|
+
spacing: z24.number().min(0.05).max(2).default(0.82),
|
|
5270
5531
|
/** How far a sheet leans back off vertical, degrees. */
|
|
5271
|
-
lean:
|
|
5532
|
+
lean: z24.number().min(0).max(70).default(16),
|
|
5272
5533
|
/** How much that lean differs sheet to sheet — nothing propped is uniform. */
|
|
5273
|
-
vary:
|
|
5534
|
+
vary: z24.number().min(0).max(1).default(0.55),
|
|
5274
5535
|
/** Small rotations off square. */
|
|
5275
|
-
sway:
|
|
5276
|
-
seed:
|
|
5536
|
+
sway: z24.number().min(0).max(1).default(0.35),
|
|
5537
|
+
seed: z24.number().int().min(0).max(9999).default(5)
|
|
5277
5538
|
});
|
|
5278
5539
|
var rack = {
|
|
5279
5540
|
id: "rack",
|
|
@@ -5297,19 +5558,19 @@ var rack = {
|
|
|
5297
5558
|
};
|
|
5298
5559
|
}
|
|
5299
5560
|
};
|
|
5300
|
-
var colonnadeSchema =
|
|
5561
|
+
var colonnadeSchema = z24.object({
|
|
5301
5562
|
/** The walk the colonnade is built along — see `stage/path`. */
|
|
5302
5563
|
path: walkPathSchema.default({}),
|
|
5303
5564
|
/** Half-width of the clear aisle: how far each banner stands off the walk line. */
|
|
5304
|
-
aisle:
|
|
5565
|
+
aisle: z24.number().min(0.2).max(20).default(2.4),
|
|
5305
5566
|
/** How much that gap opens and closes along the walk. Nothing hung by hand is a corridor. */
|
|
5306
|
-
breathe:
|
|
5567
|
+
breathe: z24.number().min(0).max(1).default(0.3),
|
|
5307
5568
|
/** Widest angle a banner turns off square to the aisle, degrees. */
|
|
5308
|
-
twist:
|
|
5569
|
+
twist: z24.number().min(0).max(90).default(22),
|
|
5309
5570
|
/** Fraction of the walk left clear at each end, so the figure has somewhere to enter from. */
|
|
5310
|
-
margin:
|
|
5571
|
+
margin: z24.number().min(0).max(0.45).default(0.05),
|
|
5311
5572
|
/** Spread of banner heights, 0..1. */
|
|
5312
|
-
rise:
|
|
5573
|
+
rise: z24.number().min(0).max(1).default(0.28),
|
|
5313
5574
|
/**
|
|
5314
5575
|
* How far the banners lift off the floor, as a fraction of their height.
|
|
5315
5576
|
*
|
|
@@ -5323,10 +5584,10 @@ var colonnadeSchema = z25.object({
|
|
|
5323
5584
|
* The bound used to be 0, so the one thing this option documented itself
|
|
5324
5585
|
* as doing was the one thing it could not do.
|
|
5325
5586
|
*/
|
|
5326
|
-
hover:
|
|
5587
|
+
hover: z24.number().min(-0.5).max(1).default(0),
|
|
5327
5588
|
/** Spread of deformation — no two lengths of hung paper drape alike. */
|
|
5328
|
-
drape:
|
|
5329
|
-
seed:
|
|
5589
|
+
drape: z24.number().min(0).max(1).default(0.5),
|
|
5590
|
+
seed: z24.number().int().min(0).max(9999).default(2)
|
|
5330
5591
|
});
|
|
5331
5592
|
function colonnadeStop(i, n, margin) {
|
|
5332
5593
|
const side = i % 2 === 0 ? 1 : -1;
|
|
@@ -5401,10 +5662,10 @@ registerLayout(colonnade);
|
|
|
5401
5662
|
registerLayout(sheet);
|
|
5402
5663
|
|
|
5403
5664
|
// src/PaperField.tsx
|
|
5404
|
-
import * as
|
|
5665
|
+
import * as THREE16 from "three";
|
|
5405
5666
|
import { gsap as gsap5 } from "gsap";
|
|
5406
|
-
import { Canvas, useFrame as useFrame5, useThree as
|
|
5407
|
-
import { forwardRef as forwardRef2, useEffect as
|
|
5667
|
+
import { Canvas, useFrame as useFrame5, useThree as useThree5 } from "@react-three/fiber";
|
|
5668
|
+
import { forwardRef as forwardRef2, useEffect as useEffect13, useMemo as useMemo9, useRef as useRef8 } from "react";
|
|
5408
5669
|
|
|
5409
5670
|
// src/field/slots.ts
|
|
5410
5671
|
var EMPTY_SET = /* @__PURE__ */ new Set();
|
|
@@ -5452,24 +5713,24 @@ function resolveFieldSlotConfig(slot, fallback, index, layoutId, layoutOptions)
|
|
|
5452
5713
|
}
|
|
5453
5714
|
|
|
5454
5715
|
// src/field/fieldGroup.tsx
|
|
5455
|
-
import * as
|
|
5716
|
+
import * as THREE13 from "three";
|
|
5456
5717
|
import { gsap as gsap3 } from "gsap";
|
|
5457
5718
|
import { useFrame as useFrame3 } from "@react-three/fiber";
|
|
5458
|
-
import { useEffect as
|
|
5719
|
+
import { useEffect as useEffect10, useMemo as useMemo6, useRef as useRef6 } from "react";
|
|
5459
5720
|
import CustomShaderMaterial2 from "three-custom-shader-material";
|
|
5460
5721
|
|
|
5461
5722
|
// src/content/atlas.ts
|
|
5462
|
-
import * as
|
|
5463
|
-
import { useEffect as
|
|
5723
|
+
import * as THREE12 from "three";
|
|
5724
|
+
import { useEffect as useEffect9, useState as useState5 } from "react";
|
|
5464
5725
|
var MAX_ATLAS = 4096;
|
|
5465
5726
|
function atlasGrid(count, aspect = 1) {
|
|
5466
5727
|
const cols = Math.max(1, Math.min(count, Math.ceil(Math.sqrt(count * Math.max(aspect, 0.01)))));
|
|
5467
5728
|
return { cols, rows: Math.max(1, Math.ceil(count / cols)) };
|
|
5468
5729
|
}
|
|
5469
5730
|
function useContentAtlas(contents, sheet2, stock) {
|
|
5470
|
-
const [atlas, setAtlas] =
|
|
5731
|
+
const [atlas, setAtlas] = useState5(null);
|
|
5471
5732
|
const stableContents = useStable(contents);
|
|
5472
|
-
|
|
5733
|
+
useEffect9(() => {
|
|
5473
5734
|
let disposed = false;
|
|
5474
5735
|
const aspect = sheet2.height / sheet2.width;
|
|
5475
5736
|
const { cols, rows } = atlasGrid(contents.length, aspect);
|
|
@@ -5485,8 +5746,8 @@ function useContentAtlas(contents, sheet2, stock) {
|
|
|
5485
5746
|
const ctx = canvas.getContext("2d");
|
|
5486
5747
|
ctx.fillStyle = stock.color;
|
|
5487
5748
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
5488
|
-
const texture = new
|
|
5489
|
-
texture.colorSpace =
|
|
5749
|
+
const texture = new THREE12.CanvasTexture(canvas);
|
|
5750
|
+
texture.colorSpace = THREE12.SRGBColorSpace;
|
|
5490
5751
|
texture.anisotropy = 4;
|
|
5491
5752
|
setAtlas({ texture, cols, rows });
|
|
5492
5753
|
const drawTile = (index, tile) => {
|
|
@@ -5655,7 +5916,7 @@ function fieldShapeStack(config, progress) {
|
|
|
5655
5916
|
|
|
5656
5917
|
// src/field/fieldGroup.tsx
|
|
5657
5918
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
5658
|
-
var scratchObj = new
|
|
5919
|
+
var scratchObj = new THREE13.Object3D();
|
|
5659
5920
|
var scratchAero = { position: [0, 0, 0], rotation: [0, 0, 0] };
|
|
5660
5921
|
var FIELD_SEGMENT_CAP = 48;
|
|
5661
5922
|
var FIELD_AUTO_CEILING = 128;
|
|
@@ -5716,28 +5977,28 @@ function FieldGroup({
|
|
|
5716
5977
|
atlasIdx[i] = i;
|
|
5717
5978
|
phase[i] = indices[i] * 0.618034 % 1 * 4;
|
|
5718
5979
|
}
|
|
5719
|
-
geo.setAttribute("aAtlas", new
|
|
5720
|
-
geo.setAttribute("aPhase", new
|
|
5721
|
-
geo.setAttribute("aBias", new
|
|
5980
|
+
geo.setAttribute("aAtlas", new THREE13.InstancedBufferAttribute(atlasIdx, 1));
|
|
5981
|
+
geo.setAttribute("aPhase", new THREE13.InstancedBufferAttribute(phase, 1));
|
|
5982
|
+
geo.setAttribute("aBias", new THREE13.InstancedBufferAttribute(bias, 1));
|
|
5722
5983
|
return geo;
|
|
5723
5984
|
}, [JSON.stringify(config.sheet), structureKey, count, autoCeiling]);
|
|
5724
|
-
|
|
5985
|
+
useEffect10(() => () => geometry.dispose(), [geometry]);
|
|
5725
5986
|
const atlas = useContentAtlas(contents, config.sheet, stock);
|
|
5726
5987
|
const shader = useMemo6(() => {
|
|
5727
5988
|
const composed = buildDisplacementGLSL(initialStack, config.sheet);
|
|
5728
5989
|
const uniforms = {};
|
|
5729
5990
|
for (const [name, value] of Object.entries(composed.uniforms)) {
|
|
5730
5991
|
uniforms[name] = {
|
|
5731
|
-
value: Array.isArray(value) && value.length === 2 ? new
|
|
5992
|
+
value: Array.isArray(value) && value.length === 2 ? new THREE13.Vector2(...value) : value
|
|
5732
5993
|
};
|
|
5733
5994
|
}
|
|
5734
5995
|
uniforms.uPlTime = { value: 0 };
|
|
5735
5996
|
uniforms.uAtlas = { value: null };
|
|
5736
|
-
uniforms.uAtlasGrid = { value: new
|
|
5997
|
+
uniforms.uAtlasGrid = { value: new THREE13.Vector2(1, 1) };
|
|
5737
5998
|
uniforms.uBackDarken = {
|
|
5738
5999
|
value: 1 - Math.min(0.45, 0.12 + config.sheet.thickness * 0.9) * stock.opacity
|
|
5739
6000
|
};
|
|
5740
|
-
uniforms.uStockColor = { value: new
|
|
6001
|
+
uniforms.uStockColor = { value: new THREE13.Color(stock.color) };
|
|
5741
6002
|
uniforms.uShowThrough = { value: config.surface.showThrough ?? stock.showThrough };
|
|
5742
6003
|
Object.assign(uniforms, translucencyUniforms(config.surface.translucency ?? stock.translucency, rig));
|
|
5743
6004
|
return {
|
|
@@ -5752,19 +6013,19 @@ function FieldGroup({
|
|
|
5752
6013
|
config.surface.showThrough,
|
|
5753
6014
|
config.surface.translucency
|
|
5754
6015
|
]);
|
|
5755
|
-
|
|
6016
|
+
useEffect10(() => {
|
|
5756
6017
|
const values = translucencyValues(config.surface.translucency ?? stock.translucency, rig);
|
|
5757
6018
|
shader.uniforms.uTranslucency.value = values.translucency;
|
|
5758
6019
|
shader.uniforms.uBackLightDir.value.copy(values.direction);
|
|
5759
6020
|
shader.uniforms.uBackLightColor.value.copy(values.color);
|
|
5760
6021
|
shader.uniforms.uAmbientTransmission.value = values.ambient;
|
|
5761
6022
|
}, [shader, rig, config.surface.translucency, stock.translucency]);
|
|
5762
|
-
|
|
6023
|
+
useEffect10(() => {
|
|
5763
6024
|
if (!atlas) return;
|
|
5764
6025
|
shader.uniforms.uAtlas.value = atlas.texture;
|
|
5765
6026
|
shader.uniforms.uAtlasGrid.value.set(atlas.cols, atlas.rows);
|
|
5766
6027
|
}, [atlas, shader]);
|
|
5767
|
-
|
|
6028
|
+
useEffect10(() => {
|
|
5768
6029
|
if (!behavior || shared.reduced) return;
|
|
5769
6030
|
const state = { p: progressRef.current };
|
|
5770
6031
|
const tween = gsap3.to(state, {
|
|
@@ -5792,7 +6053,7 @@ function FieldGroup({
|
|
|
5792
6053
|
for (const [name, value] of Object.entries(values)) {
|
|
5793
6054
|
const uniform = shader.uniforms[name];
|
|
5794
6055
|
if (!uniform) continue;
|
|
5795
|
-
if (uniform.value instanceof
|
|
6056
|
+
if (uniform.value instanceof THREE13.Vector2 && Array.isArray(value)) {
|
|
5796
6057
|
uniform.value.set(value[0], value[1]);
|
|
5797
6058
|
} else {
|
|
5798
6059
|
uniform.value = value;
|
|
@@ -5876,13 +6137,13 @@ function FieldGroup({
|
|
|
5876
6137
|
children: /* @__PURE__ */ jsx7(
|
|
5877
6138
|
CustomShaderMaterial2,
|
|
5878
6139
|
{
|
|
5879
|
-
baseMaterial:
|
|
6140
|
+
baseMaterial: THREE13.MeshStandardMaterial,
|
|
5880
6141
|
vertexShader: shader.vertexShader,
|
|
5881
6142
|
fragmentShader: shader.fragmentShader,
|
|
5882
6143
|
uniforms: shader.uniforms,
|
|
5883
6144
|
roughness: stock.roughness,
|
|
5884
6145
|
metalness: 0,
|
|
5885
|
-
side:
|
|
6146
|
+
side: THREE13.DoubleSide
|
|
5886
6147
|
},
|
|
5887
6148
|
`${structureKey}:${count}`
|
|
5888
6149
|
)
|
|
@@ -5928,8 +6189,8 @@ var easeOut = (t) => 1 - (1 - t) ** 3;
|
|
|
5928
6189
|
var easeInOut = (t) => t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;
|
|
5929
6190
|
|
|
5930
6191
|
// src/field/backingSheet.tsx
|
|
5931
|
-
import * as
|
|
5932
|
-
import { useEffect as
|
|
6192
|
+
import * as THREE14 from "three";
|
|
6193
|
+
import { useEffect as useEffect11, useMemo as useMemo7 } from "react";
|
|
5933
6194
|
|
|
5934
6195
|
// src/content/backing.ts
|
|
5935
6196
|
function silhouetteRects(o, count) {
|
|
@@ -6010,14 +6271,14 @@ function BackingSheet({
|
|
|
6010
6271
|
c.height = Math.max(2, Math.round(height * scale));
|
|
6011
6272
|
return c;
|
|
6012
6273
|
}, [width, height]);
|
|
6013
|
-
const texture = useMemo7(() => canvas ? new
|
|
6274
|
+
const texture = useMemo7(() => canvas ? new THREE14.CanvasTexture(canvas) : null, [canvas]);
|
|
6014
6275
|
const removedKey = [...removed].sort((a, b) => a - b).join(",");
|
|
6015
|
-
|
|
6276
|
+
useEffect11(() => {
|
|
6016
6277
|
if (!canvas || !texture) return;
|
|
6017
6278
|
drawBacking(canvas, { options, count, tint: BACKING_TINT, removed });
|
|
6018
6279
|
texture.needsUpdate = true;
|
|
6019
6280
|
}, [canvas, texture, JSON.stringify(options), count, removedKey]);
|
|
6020
|
-
|
|
6281
|
+
useEffect11(() => () => texture?.dispose(), [texture]);
|
|
6021
6282
|
return /* @__PURE__ */ jsxs4("mesh", { receiveShadow: true, children: [
|
|
6022
6283
|
/* @__PURE__ */ jsx8("planeGeometry", { args: [width, height] }),
|
|
6023
6284
|
/* @__PURE__ */ jsx8("meshStandardMaterial", { map: texture, color: "#ffffff", roughness: 0.92, metalness: 0 })
|
|
@@ -6025,10 +6286,10 @@ function BackingSheet({
|
|
|
6025
6286
|
}
|
|
6026
6287
|
|
|
6027
6288
|
// src/field/interactiveField.tsx
|
|
6028
|
-
import * as
|
|
6289
|
+
import * as THREE15 from "three";
|
|
6029
6290
|
import { gsap as gsap4 } from "gsap";
|
|
6030
|
-
import { useFrame as useFrame4, useThree as
|
|
6031
|
-
import { useContext as useContext3, useEffect as
|
|
6291
|
+
import { useFrame as useFrame4, useThree as useThree4 } from "@react-three/fiber";
|
|
6292
|
+
import { useContext as useContext3, useEffect as useEffect12, useMemo as useMemo8, useRef as useRef7, useState as useState6 } from "react";
|
|
6032
6293
|
import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
6033
6294
|
var PICK_BEHAVIORS = /* @__PURE__ */ new Set(["peel", "carry"]);
|
|
6034
6295
|
function InteractiveField(props) {
|
|
@@ -6038,12 +6299,12 @@ function InteractiveField(props) {
|
|
|
6038
6299
|
const reduced = usePrefersReducedMotion(props.reducedMotion);
|
|
6039
6300
|
const contextRegistry = useContext3(DropZoneContext);
|
|
6040
6301
|
const registry4 = useMemo8(() => contextRegistry ?? new DropZoneRegistry(), [contextRegistry]);
|
|
6041
|
-
const camera =
|
|
6042
|
-
const gl =
|
|
6043
|
-
const controls =
|
|
6044
|
-
const [removed, setRemoved] =
|
|
6045
|
-
const [slotPatches, setSlotPatches] =
|
|
6046
|
-
const [slotStates, setSlotStates] =
|
|
6302
|
+
const camera = useThree4((s) => s.camera);
|
|
6303
|
+
const gl = useThree4((s) => s.gl);
|
|
6304
|
+
const controls = useThree4((s) => s.controls);
|
|
6305
|
+
const [removed, setRemoved] = useState6(EMPTY_SET);
|
|
6306
|
+
const [slotPatches, setSlotPatches] = useState6({});
|
|
6307
|
+
const [slotStates, setSlotStates] = useState6({});
|
|
6047
6308
|
const slotConfigs = useMemo8(
|
|
6048
6309
|
() => papers.map((slot, i) => {
|
|
6049
6310
|
const config = resolveFieldSlotConfig(slot, fallback, i, layoutId, layoutOptions);
|
|
@@ -6078,10 +6339,10 @@ function InteractiveField(props) {
|
|
|
6078
6339
|
}
|
|
6079
6340
|
props.onSlotStateChange?.(i, state);
|
|
6080
6341
|
};
|
|
6081
|
-
const raycaster = useMemo8(() => new
|
|
6082
|
-
const planeScratch = useMemo8(() => new
|
|
6083
|
-
const pointScratch = useMemo8(() => new
|
|
6084
|
-
const ndcScratch = useMemo8(() => new
|
|
6342
|
+
const raycaster = useMemo8(() => new THREE15.Raycaster(), []);
|
|
6343
|
+
const planeScratch = useMemo8(() => new THREE15.Plane(new THREE15.Vector3(0, 0, 1), 0), []);
|
|
6344
|
+
const pointScratch = useMemo8(() => new THREE15.Vector3(), []);
|
|
6345
|
+
const ndcScratch = useMemo8(() => new THREE15.Vector2(), []);
|
|
6085
6346
|
const planePoint = (clientX, clientY, planeZ) => {
|
|
6086
6347
|
const rect = gl.domElement.getBoundingClientRect();
|
|
6087
6348
|
ndcScratch.set(
|
|
@@ -6244,12 +6505,12 @@ function InteractiveField(props) {
|
|
|
6244
6505
|
if (carried.pointerId !== null && e.pointerId !== carried.pointerId) return;
|
|
6245
6506
|
const name = slotName(carried.slot);
|
|
6246
6507
|
const zone = zonesLive().find(
|
|
6247
|
-
(
|
|
6508
|
+
(z25) => zoneContains(z25, carried.x.value, carried.y.value) && zoneAccepts(z25, name)
|
|
6248
6509
|
);
|
|
6249
6510
|
if (zone) settleInto(carried, zone);
|
|
6250
6511
|
else returnHome(carried);
|
|
6251
6512
|
};
|
|
6252
|
-
|
|
6513
|
+
useEffect12(() => {
|
|
6253
6514
|
const move = (e) => onPointerMoveRef.current(e);
|
|
6254
6515
|
const up = (e) => onPointerUpRef.current(e);
|
|
6255
6516
|
window.addEventListener("pointermove", move);
|
|
@@ -6285,12 +6546,12 @@ function InteractiveField(props) {
|
|
|
6285
6546
|
group.position.z = carried.homePose.position[2] + 0.15;
|
|
6286
6547
|
handle.set("drive", carryDrive(speed));
|
|
6287
6548
|
const lag = 0.25;
|
|
6288
|
-
group.rotation.y += (
|
|
6289
|
-
group.rotation.x += (
|
|
6549
|
+
group.rotation.y += (THREE15.MathUtils.clamp(-vx * lag, -0.6, 0.6) - group.rotation.y) * 0.12;
|
|
6550
|
+
group.rotation.x += (THREE15.MathUtils.clamp(vy * lag * 0.7, -0.5, 0.5) - group.rotation.x) * 0.12;
|
|
6290
6551
|
}
|
|
6291
6552
|
const name = slotName(carried.slot);
|
|
6292
6553
|
const zone = zonesLive().find(
|
|
6293
|
-
(
|
|
6554
|
+
(z25) => zoneContains(z25, group.position.x, group.position.y) && zoneAccepts(z25, name)
|
|
6294
6555
|
);
|
|
6295
6556
|
registry4.setHovered(zone?.id ?? null);
|
|
6296
6557
|
const targetScale = (zone ? 1.03 : 1) * carried.homePose.scale;
|
|
@@ -6306,7 +6567,7 @@ function InteractiveField(props) {
|
|
|
6306
6567
|
},
|
|
6307
6568
|
placeAtZone: (slot, zoneId) => {
|
|
6308
6569
|
const carried = carriedRef.current;
|
|
6309
|
-
const zone = zonesLive().find((
|
|
6570
|
+
const zone = zonesLive().find((z25) => z25.id === zoneId);
|
|
6310
6571
|
const group = groupRefs.current[slot];
|
|
6311
6572
|
if (!carried || carried.slot !== slot || !zone || !group) return;
|
|
6312
6573
|
group.position.x = zone.bounds.position[0];
|
|
@@ -6319,10 +6580,10 @@ function InteractiveField(props) {
|
|
|
6319
6580
|
const carried = carriedRef.current;
|
|
6320
6581
|
if (carried && carried.slot === slot && !carried.settling) returnHome(carried);
|
|
6321
6582
|
},
|
|
6322
|
-
zoneIds: () => zonesLive().map((
|
|
6583
|
+
zoneIds: () => zonesLive().map((z25) => z25.id),
|
|
6323
6584
|
slotState: (slot) => slotStates[slot] ?? "rest"
|
|
6324
6585
|
};
|
|
6325
|
-
|
|
6586
|
+
useEffect12(() => {
|
|
6326
6587
|
const ref = props.a11yRef;
|
|
6327
6588
|
if (!ref) return;
|
|
6328
6589
|
ref.current = {
|
|
@@ -6375,7 +6636,7 @@ function InteractiveField(props) {
|
|
|
6375
6636
|
}
|
|
6376
6637
|
|
|
6377
6638
|
// src/field/keyboardMirror.tsx
|
|
6378
|
-
import { useState as
|
|
6639
|
+
import { useState as useState7 } from "react";
|
|
6379
6640
|
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
6380
6641
|
function fieldKeyboardStep(carry2, slot, key, controller) {
|
|
6381
6642
|
if (!carry2) {
|
|
@@ -6421,7 +6682,7 @@ function FieldKeyboardMirror({
|
|
|
6421
6682
|
papers,
|
|
6422
6683
|
controller
|
|
6423
6684
|
}) {
|
|
6424
|
-
const [carrying, setCarrying] =
|
|
6685
|
+
const [carrying, setCarrying] = useState7(null);
|
|
6425
6686
|
const paperLabel = (slot, i) => {
|
|
6426
6687
|
try {
|
|
6427
6688
|
const config = resolveConfig({ preset: slot.preset });
|
|
@@ -6553,11 +6814,11 @@ var PaperFieldMesh = forwardRef2(
|
|
|
6553
6814
|
const mountTimeRef = useRef8(-1);
|
|
6554
6815
|
const morphRef = useRef8({ from: null, t: 1 });
|
|
6555
6816
|
const prevLayout = useRef8({ id: layoutId, options: layoutOptions });
|
|
6556
|
-
const gl =
|
|
6817
|
+
const gl = useThree5((s) => s.gl);
|
|
6557
6818
|
const driver = reduced ? "none" : props.motion?.driver ?? "autoplay";
|
|
6558
6819
|
const speed = props.motion?.speed ?? 0.5;
|
|
6559
6820
|
const entranceType = reduced ? "none" : props.entrance?.type ?? "rise";
|
|
6560
|
-
|
|
6821
|
+
useEffect13(() => {
|
|
6561
6822
|
const prev = prevLayout.current;
|
|
6562
6823
|
if (prev.id !== layoutId || JSON.stringify(prev.options) !== JSON.stringify(layoutOptions)) {
|
|
6563
6824
|
morphRef.current = { from: prev, t: 0 };
|
|
@@ -6565,7 +6826,7 @@ var PaperFieldMesh = forwardRef2(
|
|
|
6565
6826
|
prevLayout.current = { id: layoutId, options: layoutOptions };
|
|
6566
6827
|
}
|
|
6567
6828
|
}, [layoutId, layoutOptions]);
|
|
6568
|
-
|
|
6829
|
+
useEffect13(() => {
|
|
6569
6830
|
if (driver !== "drag") return;
|
|
6570
6831
|
const el = gl.domElement;
|
|
6571
6832
|
let lastX = null;
|
|
@@ -6649,9 +6910,9 @@ var PaperFieldMesh = forwardRef2(
|
|
|
6649
6910
|
}
|
|
6650
6911
|
);
|
|
6651
6912
|
function FitCamera(meshProps) {
|
|
6652
|
-
const camera =
|
|
6653
|
-
const width =
|
|
6654
|
-
const height =
|
|
6913
|
+
const camera = useThree5((s) => s.camera);
|
|
6914
|
+
const width = useThree5((s) => s.size.width);
|
|
6915
|
+
const height = useThree5((s) => s.size.height);
|
|
6655
6916
|
const papersProp = useStable(meshProps.papers ?? null);
|
|
6656
6917
|
const imagesProp = useStable(meshProps.images ?? null);
|
|
6657
6918
|
const presetProp = useStable(meshProps.preset ?? null);
|
|
@@ -6664,8 +6925,8 @@ function FitCamera(meshProps) {
|
|
|
6664
6925
|
const options = resolveLayoutOptions(layoutId, layout, optionsProp, sheet2);
|
|
6665
6926
|
return { layout, n: papers.length, options, sheet: sheet2 ?? DEFAULT_SHEET };
|
|
6666
6927
|
}, [papersProp, imagesProp, presetProp, meshProps.layout, optionsProp]);
|
|
6667
|
-
|
|
6668
|
-
if (!(camera instanceof
|
|
6928
|
+
useEffect13(() => {
|
|
6929
|
+
if (!(camera instanceof THREE16.PerspectiveCamera)) return;
|
|
6669
6930
|
const { position, target } = fitCamera(
|
|
6670
6931
|
field.layout,
|
|
6671
6932
|
field.n,
|
|
@@ -6680,7 +6941,8 @@ function FitCamera(meshProps) {
|
|
|
6680
6941
|
}, [camera, width, height, field]);
|
|
6681
6942
|
return null;
|
|
6682
6943
|
}
|
|
6683
|
-
var PaperField = forwardRef2(function PaperField2({ children, className, style, ...meshProps }, ref) {
|
|
6944
|
+
var PaperField = forwardRef2(function PaperField2({ children, className, style, scene, ...meshProps }, ref) {
|
|
6945
|
+
const rig = sceneSchema.parse(scene ?? {});
|
|
6684
6946
|
const registry4 = useMemo9(() => new DropZoneRegistry(), []);
|
|
6685
6947
|
const a11yRef = useRef8(null);
|
|
6686
6948
|
const papers = useMemo9(
|
|
@@ -6696,17 +6958,8 @@ var PaperField = forwardRef2(function PaperField2({ children, className, style,
|
|
|
6696
6958
|
return /* @__PURE__ */ jsx11("div", { className, style: { width: "100%", height: "100%", ...style }, children: /* @__PURE__ */ jsxs7(DropZoneContext.Provider, { value: registry4, children: [
|
|
6697
6959
|
/* @__PURE__ */ jsxs7(Canvas, { shadows: true, camera: { position: [0, 0.6, 5.2], fov: 45 }, dpr: [1, 2], children: [
|
|
6698
6960
|
/* @__PURE__ */ jsx11(FitCamera, { ...meshProps }),
|
|
6699
|
-
/* @__PURE__ */ jsx11(
|
|
6700
|
-
/* @__PURE__ */ jsx11(
|
|
6701
|
-
"directionalLight",
|
|
6702
|
-
{
|
|
6703
|
-
position: [3, 5, 4],
|
|
6704
|
-
intensity: 1.4,
|
|
6705
|
-
castShadow: true,
|
|
6706
|
-
"shadow-mapSize": [1024, 1024],
|
|
6707
|
-
"shadow-normalBias": 0.05
|
|
6708
|
-
}
|
|
6709
|
-
),
|
|
6961
|
+
/* @__PURE__ */ jsx11(PaperBackdrop, { backdrop: rig.backdrop }),
|
|
6962
|
+
/* @__PURE__ */ jsx11(PaperLighting, { preset: rig.lighting, light: rig.light, floor: -2.4, scale: 14 }),
|
|
6710
6963
|
/* @__PURE__ */ jsx11(PaperFieldMesh, { ref, a11yControllerRef: a11yRef, ...meshProps }),
|
|
6711
6964
|
children
|
|
6712
6965
|
] }),
|
|
@@ -6758,7 +7011,8 @@ function diffConfig(config) {
|
|
|
6758
7011
|
} else if (config.physics !== "none") {
|
|
6759
7012
|
out.physics = config.physics;
|
|
6760
7013
|
}
|
|
6761
|
-
|
|
7014
|
+
const scene = diffAgainst(config.scene, sceneSchema.parse({}));
|
|
7015
|
+
if (Object.keys(scene).length > 0) out.scene = scene;
|
|
6762
7016
|
if (config.onTwos) out.onTwos = true;
|
|
6763
7017
|
if (config.states) out.states = config.states;
|
|
6764
7018
|
const meta = diffAgainst(config.meta, paperConfigSchema.parse({}).meta);
|
|
@@ -6769,14 +7023,35 @@ function jsxValue(value) {
|
|
|
6769
7023
|
if (typeof value === "string") return `"${value}"`;
|
|
6770
7024
|
return `{${JSON.stringify(value)}}`;
|
|
6771
7025
|
}
|
|
7026
|
+
function withoutUploads(value) {
|
|
7027
|
+
let replaced = 0;
|
|
7028
|
+
const walk = (node) => {
|
|
7029
|
+
if (typeof node === "string") {
|
|
7030
|
+
if (!node.startsWith("data:")) return node;
|
|
7031
|
+
replaced++;
|
|
7032
|
+
const extension = node.startsWith("data:image/png") ? "png" : "jpg";
|
|
7033
|
+
return `/paperlab-image-${replaced}.${extension}`;
|
|
7034
|
+
}
|
|
7035
|
+
if (Array.isArray(node)) return node.map(walk);
|
|
7036
|
+
if (node && typeof node === "object") {
|
|
7037
|
+
return Object.fromEntries(Object.entries(node).map(([k, v]) => [k, walk(v)]));
|
|
7038
|
+
}
|
|
7039
|
+
return node;
|
|
7040
|
+
};
|
|
7041
|
+
return { value: walk(value), replaced };
|
|
7042
|
+
}
|
|
7043
|
+
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.";
|
|
6772
7044
|
function buildJsxSnippet(config) {
|
|
6773
|
-
const
|
|
6774
|
-
delete
|
|
7045
|
+
const diffed = diffConfig(config);
|
|
7046
|
+
delete diffed.meta;
|
|
7047
|
+
const { value: diff, replaced } = withoutUploads(diffed);
|
|
6775
7048
|
const props = Object.entries(diff).map(([key, value]) => ` ${key}=${jsxValue(value)}`);
|
|
6776
7049
|
if (props.length === 0) return "<Paper />";
|
|
6777
|
-
|
|
7050
|
+
const snippet = `<Paper
|
|
6778
7051
|
${props.join("\n")}
|
|
6779
7052
|
/>`;
|
|
7053
|
+
return replaced > 0 ? `${UPLOAD_NOTE}
|
|
7054
|
+
${snippet}` : snippet;
|
|
6780
7055
|
}
|
|
6781
7056
|
|
|
6782
7057
|
// src/config/agent-payload.ts
|
|
@@ -6826,7 +7101,10 @@ function componentName(config) {
|
|
|
6826
7101
|
}
|
|
6827
7102
|
function buildAgentPayload(config) {
|
|
6828
7103
|
const name = componentName(config);
|
|
6829
|
-
const
|
|
7104
|
+
const { value: stripped, replaced } = withoutUploads(diffConfig(config));
|
|
7105
|
+
const preset = JSON.stringify(stripped, null, 2);
|
|
7106
|
+
const uploadNote = replaced > 0 ? `${UPLOAD_NOTE}
|
|
7107
|
+
` : "";
|
|
6830
7108
|
return `Integrate a Paperlab paper component into this project. (paperlab agent-payload v${AGENT_PAYLOAD_VERSION})
|
|
6831
7109
|
|
|
6832
7110
|
1. Install the dependencies:
|
|
@@ -6839,7 +7117,7 @@ function buildAgentPayload(config) {
|
|
|
6839
7117
|
\`\`\`tsx
|
|
6840
7118
|
import { Paper, type PaperConfigInput } from 'paperlab'
|
|
6841
7119
|
|
|
6842
|
-
const preset = ${preset.replace(/\n/g, "\n")} satisfies PaperConfigInput
|
|
7120
|
+
${uploadNote}const preset = ${preset.replace(/\n/g, "\n")} satisfies PaperConfigInput
|
|
6843
7121
|
|
|
6844
7122
|
export function ${name}() {
|
|
6845
7123
|
return <Paper preset={preset} />
|
|
@@ -6869,11 +7147,17 @@ export {
|
|
|
6869
7147
|
segmentsForArc,
|
|
6870
7148
|
segmentsForSine,
|
|
6871
7149
|
stockNames,
|
|
7150
|
+
washSchema,
|
|
7151
|
+
contentNames,
|
|
7152
|
+
contentSchemaFor,
|
|
6872
7153
|
paperEdges,
|
|
6873
7154
|
behaviorConfigSchema,
|
|
6874
7155
|
physicsNames,
|
|
6875
7156
|
clothConfigSchema,
|
|
6876
7157
|
lightingNames,
|
|
7158
|
+
lightSchema,
|
|
7159
|
+
backdropSchema,
|
|
7160
|
+
sceneSchema,
|
|
6877
7161
|
coreStateNames,
|
|
6878
7162
|
stateDefSchema,
|
|
6879
7163
|
paperStatesSchema,
|
|
@@ -6897,7 +7181,6 @@ export {
|
|
|
6897
7181
|
getBehavior,
|
|
6898
7182
|
listBehaviors,
|
|
6899
7183
|
idleNames,
|
|
6900
|
-
lightSchema,
|
|
6901
7184
|
lightAngles,
|
|
6902
7185
|
resolveLighting,
|
|
6903
7186
|
LightRig,
|
|
@@ -6913,6 +7196,7 @@ export {
|
|
|
6913
7196
|
PaperMesh,
|
|
6914
7197
|
cssColorOr,
|
|
6915
7198
|
PaperLighting,
|
|
7199
|
+
PaperBackdrop,
|
|
6916
7200
|
DropZone,
|
|
6917
7201
|
sheetLayoutSchema,
|
|
6918
7202
|
buildDisplacementGLSL,
|
|
@@ -6930,4 +7214,4 @@ export {
|
|
|
6930
7214
|
describeConfig,
|
|
6931
7215
|
buildAgentPayload
|
|
6932
7216
|
};
|
|
6933
|
-
//# sourceMappingURL=chunk-
|
|
7217
|
+
//# sourceMappingURL=chunk-6IADJZX5.js.map
|