paperlab 0.3.0 → 0.4.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 +154 -45
- package/dist/{chunk-3IMUEESH.js → chunk-4ZU5DZEF.js} +1705 -1807
- package/dist/chunk-4ZU5DZEF.js.map +1 -0
- package/dist/index.cjs +493 -748
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +419 -1874
- package/dist/index.d.ts +419 -1874
- package/dist/index.js +183 -431
- package/dist/index.js.map +1 -1
- package/dist/{slots-D0Nc_5dq.d.cts → slots-BYoLxVZ4.d.cts} +3 -656
- package/dist/{slots-D0Nc_5dq.d.ts → slots-BYoLxVZ4.d.ts} +3 -656
- package/dist/stage.cjs +3 -17
- package/dist/stage.cjs.map +1 -1
- package/dist/stage.d.cts +2 -178
- package/dist/stage.d.ts +2 -178
- package/dist/stage.js +1 -8
- package/package.json +1 -1
- package/dist/chunk-3IMUEESH.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -30,197 +30,73 @@ var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: tr
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
AGENT_PAYLOAD_VERSION: () => AGENT_PAYLOAD_VERSION,
|
|
34
33
|
AUTO_CEILING: () => AUTO_CEILING,
|
|
35
|
-
ClothSim: () => ClothSim,
|
|
36
34
|
DropZone: () => DropZone,
|
|
37
|
-
DropZoneRegistry: () => DropZoneRegistry,
|
|
38
35
|
FLAT_SEGMENTS: () => FLAT_SEGMENTS,
|
|
39
36
|
LightRig: () => LightRig,
|
|
40
|
-
ON_TWOS_FPS: () => ON_TWOS_FPS,
|
|
41
37
|
PARITY_EPSILON: () => PARITY_EPSILON,
|
|
42
38
|
Paper: () => Paper,
|
|
43
|
-
PaperFallback: () => PaperFallback,
|
|
44
39
|
PaperField: () => PaperField,
|
|
45
40
|
PaperFieldMesh: () => PaperFieldMesh,
|
|
46
41
|
PaperLighting: () => PaperLighting,
|
|
47
|
-
PaperMaterial: () => PaperMaterial,
|
|
48
42
|
PaperMesh: () => PaperMesh,
|
|
49
|
-
PaperMirror: () => PaperMirror,
|
|
50
|
-
PaperStateMachine: () => PaperStateMachine,
|
|
51
43
|
SAG_TOL: () => SAG_TOL,
|
|
52
|
-
SHEET_LIFT: () => SHEET_LIFT,
|
|
53
|
-
TRANSMISSION_GAIN: () => TRANSMISSION_GAIN,
|
|
54
|
-
accordion: () => accordion,
|
|
55
|
-
applyDeformerStack: () => applyDeformerStack,
|
|
56
|
-
atlasGrid: () => atlasGrid,
|
|
57
|
-
backContentSchema: () => backContentSchema,
|
|
58
|
-
barcodeBars: () => barcodeBars,
|
|
59
44
|
behaviorConfigSchema: () => behaviorConfigSchema,
|
|
60
|
-
bend: () => bend,
|
|
61
|
-
bendOptionsSchema: () => bendOptionsSchema,
|
|
62
|
-
book: () => book,
|
|
63
45
|
buildAgentPayload: () => buildAgentPayload,
|
|
64
|
-
buildDisplacementGLSL: () => buildDisplacementGLSL,
|
|
65
46
|
buildFieldAgentPayload: () => buildFieldAgentPayload,
|
|
66
47
|
buildFieldComponentSource: () => buildFieldComponentSource,
|
|
67
|
-
buildFieldFragmentShader: () => buildFieldFragmentShader,
|
|
68
|
-
buildFieldVertexShader: () => buildFieldVertexShader,
|
|
69
48
|
buildJsxSnippet: () => buildJsxSnippet,
|
|
70
|
-
cardContentSchema: () => cardContentSchema,
|
|
71
|
-
carry: () => carry,
|
|
72
|
-
carryDrive: () => carryDrive,
|
|
73
|
-
carryOptionsSchema: () => carryOptionsSchema,
|
|
74
49
|
clothConfigSchema: () => clothConfigSchema,
|
|
75
|
-
colonnade: () => colonnade,
|
|
76
|
-
composeSurface: () => composeSurface,
|
|
77
|
-
contentSchema: () => contentSchema,
|
|
78
|
-
contentText: () => contentText,
|
|
79
50
|
coreStateNames: () => coreStateNames,
|
|
80
|
-
cornerNames: () => cornerNames,
|
|
81
|
-
createSheetGeometry: () => createSheetGeometry,
|
|
82
|
-
crumple: () => crumple,
|
|
83
|
-
crumpleBehavior: () => crumpleBehavior,
|
|
84
|
-
crumpleBehaviorOptionsSchema: () => crumpleBehaviorOptionsSchema,
|
|
85
|
-
crumpleOptionsSchema: () => crumpleOptionsSchema,
|
|
86
|
-
curl: () => curl,
|
|
87
|
-
curlOptionsSchema: () => curlOptionsSchema,
|
|
88
|
-
dampTo: () => dampTo,
|
|
89
|
-
deformerInstanceSchema: () => deformerInstanceSchema,
|
|
90
51
|
describeConfig: () => describeConfig,
|
|
91
52
|
describeFieldConfig: () => describeFieldConfig,
|
|
92
53
|
diffConfig: () => diffConfig,
|
|
93
54
|
diffFieldProps: () => diffFieldProps,
|
|
94
|
-
displacePoint: () => displacePoint,
|
|
95
|
-
distinctFieldPresets: () => distinctFieldPresets,
|
|
96
|
-
drape: () => drape,
|
|
97
|
-
drapeOptionsSchema: () => drapeOptionsSchema,
|
|
98
|
-
drawBacking: () => drawBacking,
|
|
99
|
-
fall: () => fall,
|
|
100
|
-
fallOptionsSchema: () => fallOptionsSchema,
|
|
101
|
-
fan: () => fan,
|
|
102
|
-
fieldBounds: () => fieldBounds,
|
|
103
|
-
fieldKeyboardStep: () => fieldKeyboardStep,
|
|
104
|
-
filmNames: () => filmNames,
|
|
105
|
-
fitCamera: () => fitCamera,
|
|
106
|
-
flattenNumeric: () => flattenNumeric,
|
|
107
|
-
flight: () => flight,
|
|
108
|
-
flightOptionsSchema: () => flightOptionsSchema,
|
|
109
|
-
flightPose: () => flightPose,
|
|
110
|
-
flip: () => flip,
|
|
111
|
-
flipOptionsSchema: () => flipOptionsSchema,
|
|
112
|
-
fly: () => fly,
|
|
113
|
-
flyOptionsSchema: () => flyOptionsSchema,
|
|
114
|
-
fold: () => fold,
|
|
115
|
-
foldOptionsSchema: () => foldOptionsSchema,
|
|
116
55
|
getBehavior: () => getBehavior,
|
|
117
56
|
getDeformer: () => getDeformer,
|
|
118
|
-
getIdlePreset: () => getIdlePreset,
|
|
119
57
|
getLayout: () => getLayout,
|
|
120
|
-
getLightingPreset: () => getLightingPreset,
|
|
121
58
|
getPreset: () => getPreset,
|
|
122
59
|
getStock: () => getStock,
|
|
123
|
-
groupFieldPapers: () => groupFieldPapers,
|
|
124
|
-
gust: () => gust,
|
|
125
|
-
hang: () => hang,
|
|
126
|
-
hangOptionsSchema: () => hangOptionsSchema,
|
|
127
60
|
idleNames: () => idleNames,
|
|
128
|
-
idlePresets: () => idlePresets,
|
|
129
61
|
isBuiltinPreset: () => isBuiltinPreset,
|
|
130
|
-
letterFold: () => letterFold,
|
|
131
|
-
letterFoldOptionsSchema: () => letterFoldOptionsSchema,
|
|
132
62
|
lightAngles: () => lightAngles,
|
|
133
|
-
lightPosition: () => lightPosition,
|
|
134
63
|
lightSchema: () => lightSchema,
|
|
135
|
-
lightenHex: () => lightenHex,
|
|
136
64
|
lightingNames: () => lightingNames,
|
|
137
|
-
lightingPresets: () => lightingPresets,
|
|
138
65
|
listBehaviors: () => listBehaviors,
|
|
139
66
|
listDeformers: () => listDeformers,
|
|
140
67
|
listLayouts: () => listLayouts,
|
|
141
68
|
listPresets: () => listPresets,
|
|
142
|
-
makeGoboTexture: () => makeGoboTexture,
|
|
143
69
|
mergeConfig: () => mergeConfig,
|
|
144
70
|
mergeWithDeletes: () => mergeWithDeletes,
|
|
145
|
-
outwardCorner: () => outwardCorner,
|
|
146
71
|
paperConfigSchema: () => paperConfigSchema,
|
|
147
72
|
paperEdges: () => paperEdges,
|
|
148
73
|
paperStatesSchema: () => paperStatesSchema,
|
|
149
74
|
parityCases: () => parityCases,
|
|
150
75
|
parsePreset: () => parsePreset,
|
|
151
|
-
peel: () => peel,
|
|
152
|
-
peelOptionsSchema: () => peelOptionsSchema,
|
|
153
76
|
physicsNames: () => physicsNames,
|
|
154
|
-
physicsSchema: () => physicsSchema,
|
|
155
|
-
pile: () => pile,
|
|
156
|
-
quantizeProgress: () => quantizeProgress,
|
|
157
77
|
quantizeSegments: () => quantizeSegments,
|
|
158
|
-
quantizeTime: () => quantizeTime,
|
|
159
|
-
rack: () => rack,
|
|
160
|
-
receiptContentSchema: () => receiptContentSchema,
|
|
161
|
-
receiptTotals: () => receiptTotals,
|
|
162
78
|
recordStateOverride: () => recordStateOverride,
|
|
163
79
|
registerBehavior: () => registerBehavior,
|
|
164
80
|
registerDeformer: () => registerDeformer,
|
|
165
81
|
registerLayout: () => registerLayout,
|
|
166
82
|
registerPreset: () => registerPreset,
|
|
167
|
-
resolveConfig: () => resolveConfig,
|
|
168
|
-
resolveDeformerStack: () => resolveDeformerStack,
|
|
169
|
-
resolveFieldSlotConfig: () => resolveFieldSlotConfig,
|
|
170
83
|
resolveLighting: () => resolveLighting,
|
|
171
|
-
resolveMode: () => resolveMode,
|
|
172
|
-
resolveSegments: () => resolveSegments,
|
|
173
84
|
resolveStateConfig: () => resolveStateConfig,
|
|
174
|
-
ribbon: () => ribbon,
|
|
175
|
-
ribbonOptionsSchema: () => ribbonOptionsSchema,
|
|
176
|
-
ring: () => ring,
|
|
177
|
-
roll: () => roll,
|
|
178
|
-
rollOptionsSchema: () => rollOptionsSchema,
|
|
179
85
|
runParityHarness: () => runParityHarness,
|
|
180
|
-
sceneSchema: () => sceneSchema,
|
|
181
86
|
segmentsForArc: () => segmentsForArc,
|
|
182
87
|
segmentsForSine: () => segmentsForSine,
|
|
183
88
|
serializePreset: () => serializePreset,
|
|
184
|
-
settle: () => settle,
|
|
185
|
-
settleOptionsSchema: () => settleOptionsSchema,
|
|
186
|
-
sheet: () => sheet,
|
|
187
|
-
sheetBackingSize: () => sheetBackingSize,
|
|
188
89
|
sheetLayoutSchema: () => sheetLayoutSchema,
|
|
189
|
-
sheetSchema: () => sheetSchema,
|
|
190
|
-
sheetSlotXY: () => sheetSlotXY,
|
|
191
|
-
silhouetteRects: () => silhouetteRects,
|
|
192
90
|
spanAlong: () => spanAlong,
|
|
193
|
-
spill: () => spill,
|
|
194
|
-
spread: () => spread,
|
|
195
|
-
stackAutoSegments: () => stackAutoSegments,
|
|
196
|
-
stackMinSegments: () => stackMinSegments,
|
|
197
|
-
stackUniformValues: () => stackUniformValues,
|
|
198
91
|
stateDefSchema: () => stateDefSchema,
|
|
199
|
-
stateEventTransitions: () => stateEventTransitions,
|
|
200
|
-
stateTransitionSchema: () => stateTransitionSchema,
|
|
201
92
|
stockNames: () => stockNames,
|
|
202
|
-
stockSchema: () => stockSchema,
|
|
203
93
|
stocks: () => stocks,
|
|
204
|
-
stripStates: () => stripStates,
|
|
205
94
|
supportsWebGL: () => supportsWebGL,
|
|
206
|
-
surfaceSchema: () => surfaceSchema,
|
|
207
|
-
sweep: () => sweep,
|
|
208
|
-
tornEdgesOnDetach: () => tornEdgesOnDetach,
|
|
209
|
-
translucencyUniforms: () => translucencyUniforms,
|
|
210
|
-
translucencyValues: () => translucencyValues,
|
|
211
95
|
uniquePresetName: () => uniquePresetName,
|
|
212
96
|
unregisterPreset: () => unregisterPreset,
|
|
213
|
-
unroll: () => unroll,
|
|
214
|
-
unrollOptionsSchema: () => unrollOptionsSchema,
|
|
215
|
-
useContentAtlas: () => useContentAtlas,
|
|
216
97
|
usePaperStates: () => usePaperStates,
|
|
217
98
|
usePrefersReducedMotion: () => usePrefersReducedMotion,
|
|
218
|
-
|
|
219
|
-
wave: () => wave,
|
|
220
|
-
waveOptionsSchema: () => waveOptionsSchema,
|
|
221
|
-
withSheetCellFromPaper: () => withSheetCellFromPaper,
|
|
222
|
-
wrapLines: () => wrapLines,
|
|
223
|
-
zoneAccepts: () => zoneAccepts
|
|
99
|
+
wrapLines: () => wrapLines
|
|
224
100
|
});
|
|
225
101
|
module.exports = __toCommonJS(index_exports);
|
|
226
102
|
|
|
@@ -1394,7 +1270,7 @@ var paperConfigSchema = import_zod14.z.object({
|
|
|
1394
1270
|
physics: physicsSchema.default("none"),
|
|
1395
1271
|
scene: sceneSchema.default({}),
|
|
1396
1272
|
onTwos: import_zod14.z.boolean().default(false),
|
|
1397
|
-
/** Interaction state machine — overrides-on-base diffs
|
|
1273
|
+
/** Interaction state machine — overrides-on-base diffs. */
|
|
1398
1274
|
states: paperStatesSchema.optional()
|
|
1399
1275
|
}).superRefine((config, ctx) => {
|
|
1400
1276
|
if (typeof config.physics === "object" && (config.behavior || config.deformers)) {
|
|
@@ -1757,7 +1633,7 @@ var builtins = {
|
|
|
1757
1633
|
meta: { name: "Blank sheet", tags: ["starter"] },
|
|
1758
1634
|
stock: "printer"
|
|
1759
1635
|
},
|
|
1760
|
-
// The
|
|
1636
|
+
// The driving use case: one stamp of the 2×5 block. Hover peels the
|
|
1761
1637
|
// outward-facing corner ('auto' resolves per sheet slot), pressing deepens
|
|
1762
1638
|
// the peel; the perforation tears when it detaches (field auto-wiring).
|
|
1763
1639
|
"postage-stamp": {
|
|
@@ -3623,7 +3499,7 @@ void plPerforation(inout vec4 color) {
|
|
|
3623
3499
|
float cell = mod(a + uPerfSpacing * 0.5, uPerfSpacing) - uPerfSpacing * 0.5;
|
|
3624
3500
|
if (uPerfTorn[e] < 0.5) {
|
|
3625
3501
|
// Intact: clean semicircular punches on the edge line (alphaTest, not
|
|
3626
|
-
// blending \u2014 shadow correctness
|
|
3502
|
+
// blending \u2014 shadow correctness).
|
|
3627
3503
|
if (length(vec2(cell, d)) < uPerfRadius) color.a = 0.0;
|
|
3628
3504
|
} else {
|
|
3629
3505
|
// Torn: ripped profile following the hole rhythm \u2014 alternating tabs and
|
|
@@ -6975,307 +6851,6 @@ var PaperField = (0, import_react16.forwardRef)(function PaperField2({ children,
|
|
|
6975
6851
|
] }) });
|
|
6976
6852
|
});
|
|
6977
6853
|
|
|
6978
|
-
// src/field/parity.ts
|
|
6979
|
-
var THREE16 = __toESM(require("three"), 1);
|
|
6980
|
-
var GRID = 48;
|
|
6981
|
-
var PARITY_EPSILON = 5e-4;
|
|
6982
|
-
var parityCases = [
|
|
6983
|
-
{
|
|
6984
|
-
name: "roll: defaults",
|
|
6985
|
-
stack: [{ type: "roll", options: { angle: 90, boundary: 0, radius: 0.12, spiral: 0.015 } }],
|
|
6986
|
-
sheet: { width: 1, height: 1.4 },
|
|
6987
|
-
t: 0
|
|
6988
|
-
},
|
|
6989
|
-
{
|
|
6990
|
-
name: "roll: tight receipt roll, rolling down",
|
|
6991
|
-
stack: [{ type: "roll", options: { angle: 270, boundary: -0.4, radius: 0.07, spiral: 0.02 } }],
|
|
6992
|
-
sheet: { width: 1, height: 2.6 },
|
|
6993
|
-
t: 0
|
|
6994
|
-
},
|
|
6995
|
-
{
|
|
6996
|
-
name: "curl: bottom-right peel",
|
|
6997
|
-
stack: [{ type: "curl", options: { corner: "bottom-right", amount: 0.45, radius: 0.2, skew: 0 } }],
|
|
6998
|
-
sheet: { width: 1.5, height: 1 },
|
|
6999
|
-
t: 0
|
|
7000
|
-
},
|
|
7001
|
-
{
|
|
7002
|
-
name: "curl: skewed top-left",
|
|
7003
|
-
stack: [{ type: "curl", options: { corner: "top-left", amount: 0.7, radius: 0.3, skew: 15 } }],
|
|
7004
|
-
sheet: { width: 1, height: 1 },
|
|
7005
|
-
t: 0
|
|
7006
|
-
},
|
|
7007
|
-
{
|
|
7008
|
-
name: "bend: positive arc at an angle",
|
|
7009
|
-
stack: [{ type: "bend", options: { curvature: 1.2, angle: 33 } }],
|
|
7010
|
-
sheet: { width: 1, height: 1.4 },
|
|
7011
|
-
t: 0
|
|
7012
|
-
},
|
|
7013
|
-
{
|
|
7014
|
-
/**
|
|
7015
|
-
* The gentle end, which nothing used to cover: the gate only ever tried
|
|
7016
|
-
* |curvature| ≥ 0.6, and `photo-print` — the field starter preset — bends
|
|
7017
|
-
* at 0.35. That band is where the arc's float32 evaluation loses its
|
|
7018
|
-
* cancellation, and where the two paths were 6e-4 apart until `bend` was
|
|
7019
|
-
* rewritten in cancellation-free form. Keep a case down here.
|
|
7020
|
-
*/
|
|
7021
|
-
name: "bend: barely-there arc (the band photo-print lives in)",
|
|
7022
|
-
stack: [{ type: "bend", options: { curvature: 0.35, angle: 0 } }],
|
|
7023
|
-
sheet: { width: 1.2, height: 0.9 },
|
|
7024
|
-
t: 0
|
|
7025
|
-
},
|
|
7026
|
-
{
|
|
7027
|
-
name: "bend: the gentlest arc the schema allows",
|
|
7028
|
-
stack: [{ type: "bend", options: { curvature: 0.02, angle: 61 } }],
|
|
7029
|
-
sheet: { width: 2, height: 2.6 },
|
|
7030
|
-
t: 0
|
|
7031
|
-
},
|
|
7032
|
-
{
|
|
7033
|
-
name: "bend: negative arc",
|
|
7034
|
-
stack: [{ type: "bend", options: { curvature: -0.8, angle: 0 } }],
|
|
7035
|
-
sheet: { width: 1, height: 1.4 },
|
|
7036
|
-
t: 0
|
|
7037
|
-
},
|
|
7038
|
-
{
|
|
7039
|
-
name: "fold: 90\xB0 hinge",
|
|
7040
|
-
stack: [{ type: "fold", options: { angle: 90, offset: 0, foldAngle: 90, radius: 0.06 } }],
|
|
7041
|
-
sheet: { width: 1, height: 1.4 },
|
|
7042
|
-
t: 0
|
|
7043
|
-
},
|
|
7044
|
-
{
|
|
7045
|
-
name: "fold: deep fold, travelling down",
|
|
7046
|
-
stack: [{ type: "fold", options: { angle: 270, offset: 0.2, foldAngle: 165, radius: 0.04 } }],
|
|
7047
|
-
sheet: { width: 1, height: 1.4 },
|
|
7048
|
-
t: 0
|
|
7049
|
-
},
|
|
7050
|
-
{
|
|
7051
|
-
name: "wave: free ripple at t=1.234",
|
|
7052
|
-
stack: [
|
|
7053
|
-
{
|
|
7054
|
-
type: "wave",
|
|
7055
|
-
options: { amplitude: 0.05, wavelength: 0.5, speed: 1, angle: 20, pinnedEdge: "none" }
|
|
7056
|
-
}
|
|
7057
|
-
],
|
|
7058
|
-
sheet: { width: 1, height: 1 },
|
|
7059
|
-
t: 1.234
|
|
7060
|
-
},
|
|
7061
|
-
{
|
|
7062
|
-
name: "wave: pinned top at t=2.5",
|
|
7063
|
-
stack: [
|
|
7064
|
-
{
|
|
7065
|
-
type: "wave",
|
|
7066
|
-
options: { amplitude: 0.04, wavelength: 0.4, speed: 1.5, angle: 80, pinnedEdge: "top" }
|
|
7067
|
-
}
|
|
7068
|
-
],
|
|
7069
|
-
sheet: { width: 1.2, height: 1.5 },
|
|
7070
|
-
t: 2.5
|
|
7071
|
-
},
|
|
7072
|
-
{
|
|
7073
|
-
name: "drape: banner hung from the top",
|
|
7074
|
-
stack: [
|
|
7075
|
-
{
|
|
7076
|
-
type: "drape",
|
|
7077
|
-
options: { amplitude: 0.17, folds: 5, falloff: 1.6, irregular: 0.45, gather: 0.5, pinnedEdge: "top" }
|
|
7078
|
-
}
|
|
7079
|
-
],
|
|
7080
|
-
sheet: { width: 1.5, height: 8.5 },
|
|
7081
|
-
t: 0
|
|
7082
|
-
},
|
|
7083
|
-
{
|
|
7084
|
-
name: "drape: deep irregular folds pinned at the bottom",
|
|
7085
|
-
stack: [
|
|
7086
|
-
{
|
|
7087
|
-
type: "drape",
|
|
7088
|
-
options: { amplitude: 0.55, folds: 11, falloff: 0.4, irregular: 1, gather: 1, pinnedEdge: "bottom" }
|
|
7089
|
-
}
|
|
7090
|
-
],
|
|
7091
|
-
sheet: { width: 2.2, height: 3 },
|
|
7092
|
-
t: 0
|
|
7093
|
-
},
|
|
7094
|
-
{
|
|
7095
|
-
name: "crumple: defaults",
|
|
7096
|
-
stack: [{ type: "crumple", options: { amount: 0.35, scale: 3, pull: 0.4, seed: 0 } }],
|
|
7097
|
-
sheet: { width: 1, height: 1.4 },
|
|
7098
|
-
t: 0
|
|
7099
|
-
},
|
|
7100
|
-
{
|
|
7101
|
-
// The adversarial one. `fract` is a sawtooth, so the two halves disagree
|
|
7102
|
-
// hardest where a crease lands exactly on a sample — a fine scale and a
|
|
7103
|
-
// seed whose fold directions are near-axis puts the most creases in
|
|
7104
|
-
// reach of the grid.
|
|
7105
|
-
name: "crumple: fully crushed, fine creases, off-axis seed",
|
|
7106
|
-
stack: [{ type: "crumple", options: { amount: 1, scale: 7.5, pull: 1, seed: 5 } }],
|
|
7107
|
-
sheet: { width: 1.3, height: 0.9 },
|
|
7108
|
-
t: 0
|
|
7109
|
-
},
|
|
7110
|
-
{
|
|
7111
|
-
// Crush then curl, which is the order the crumple BEHAVIOR stacks them:
|
|
7112
|
-
// the creases have to be placed on the flat sheet, not on a bent one.
|
|
7113
|
-
name: "stacked: crumple \u2218 bend (the crumple behavior)",
|
|
7114
|
-
stack: [
|
|
7115
|
-
{ type: "crumple", options: { amount: 0.62, scale: 3.3, pull: 0.5, seed: 2 } },
|
|
7116
|
-
{ type: "bend", options: { curvature: 0.31, angle: 35 } }
|
|
7117
|
-
],
|
|
7118
|
-
sheet: { width: 1.1, height: 1.4 },
|
|
7119
|
-
t: 0
|
|
7120
|
-
},
|
|
7121
|
-
{
|
|
7122
|
-
name: "stacked: letter-fold pair (fold \u2218 fold)",
|
|
7123
|
-
stack: [
|
|
7124
|
-
{ type: "fold", options: { angle: 270, offset: 0.2333, foldAngle: 120, radius: 0.05 } },
|
|
7125
|
-
{ type: "fold", options: { angle: 90, offset: 0.2333, foldAngle: 100, radius: 0.08 } }
|
|
7126
|
-
],
|
|
7127
|
-
sheet: { width: 1, height: 1.4 },
|
|
7128
|
-
t: 0
|
|
7129
|
-
},
|
|
7130
|
-
{
|
|
7131
|
-
name: "stacked: bend \u2218 roll \u2218 wave",
|
|
7132
|
-
stack: [
|
|
7133
|
-
{ type: "bend", options: { curvature: 0.6, angle: 0 } },
|
|
7134
|
-
{ type: "roll", options: { angle: 90, boundary: 0.1, radius: 0.15, spiral: 0 } },
|
|
7135
|
-
{
|
|
7136
|
-
type: "wave",
|
|
7137
|
-
options: { amplitude: 0.02, wavelength: 0.6, speed: 0.7, angle: 45, pinnedEdge: "none" }
|
|
7138
|
-
}
|
|
7139
|
-
],
|
|
7140
|
-
sheet: { width: 1, height: 1.4 },
|
|
7141
|
-
t: 0.8
|
|
7142
|
-
}
|
|
7143
|
-
];
|
|
7144
|
-
function buildParityFragment(stack, sheet2) {
|
|
7145
|
-
const composed = buildDisplacementGLSL(stack, sheet2);
|
|
7146
|
-
return (
|
|
7147
|
-
/* glsl */
|
|
7148
|
-
`#version 300 es
|
|
7149
|
-
precision highp float;
|
|
7150
|
-
uniform float uPlTime;
|
|
7151
|
-
uniform float uPlBias;
|
|
7152
|
-
${composed.functionsSrc}
|
|
7153
|
-
${composed.displaceSrc}
|
|
7154
|
-
out vec4 outColor;
|
|
7155
|
-
void main() {
|
|
7156
|
-
vec2 uv = (gl_FragCoord.xy - 0.5) / float(${GRID - 1});
|
|
7157
|
-
vec3 p = vec3((uv - 0.5) * uSheet, 0.0);
|
|
7158
|
-
outColor = vec4(plDisplace(p, uv, uPlTime, uPlBias), 1.0);
|
|
7159
|
-
}
|
|
7160
|
-
`
|
|
7161
|
-
);
|
|
7162
|
-
}
|
|
7163
|
-
var VERT = `#version 300 es
|
|
7164
|
-
in vec2 aPos;
|
|
7165
|
-
void main() { gl_Position = vec4(aPos, 0.0, 1.0); }
|
|
7166
|
-
`;
|
|
7167
|
-
function compile(gl, type, src) {
|
|
7168
|
-
const shader = gl.createShader(type);
|
|
7169
|
-
gl.shaderSource(shader, src);
|
|
7170
|
-
gl.compileShader(shader);
|
|
7171
|
-
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
7172
|
-
throw new Error(`[paperlab parity] shader compile failed: ${gl.getShaderInfoLog(shader)}`);
|
|
7173
|
-
}
|
|
7174
|
-
return shader;
|
|
7175
|
-
}
|
|
7176
|
-
function runCaseOnGPU(gl, c, bias = 1) {
|
|
7177
|
-
const composed = buildDisplacementGLSL(c.stack, c.sheet);
|
|
7178
|
-
const program = gl.createProgram();
|
|
7179
|
-
gl.attachShader(program, compile(gl, gl.VERTEX_SHADER, VERT));
|
|
7180
|
-
gl.attachShader(program, compile(gl, gl.FRAGMENT_SHADER, buildParityFragment(c.stack, c.sheet)));
|
|
7181
|
-
gl.linkProgram(program);
|
|
7182
|
-
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
7183
|
-
throw new Error(`[paperlab parity] link failed: ${gl.getProgramInfoLog(program)}`);
|
|
7184
|
-
}
|
|
7185
|
-
gl.useProgram(program);
|
|
7186
|
-
for (const [name, value] of Object.entries(composed.uniforms)) {
|
|
7187
|
-
const loc = gl.getUniformLocation(program, name);
|
|
7188
|
-
if (!loc) continue;
|
|
7189
|
-
if (typeof value === "number") gl.uniform1f(loc, value);
|
|
7190
|
-
else if (value.length === 2) gl.uniform2f(loc, value[0], value[1]);
|
|
7191
|
-
else if (value.length === 3) gl.uniform3f(loc, value[0], value[1], value[2]);
|
|
7192
|
-
else gl.uniform4f(loc, value[0], value[1], value[2], value[3]);
|
|
7193
|
-
}
|
|
7194
|
-
const timeLoc = gl.getUniformLocation(program, "uPlTime");
|
|
7195
|
-
if (timeLoc) gl.uniform1f(timeLoc, c.t);
|
|
7196
|
-
const biasLoc = gl.getUniformLocation(program, "uPlBias");
|
|
7197
|
-
if (biasLoc) gl.uniform1f(biasLoc, bias);
|
|
7198
|
-
const quad = gl.createBuffer();
|
|
7199
|
-
gl.bindBuffer(gl.ARRAY_BUFFER, quad);
|
|
7200
|
-
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
|
|
7201
|
-
const posLoc = gl.getAttribLocation(program, "aPos");
|
|
7202
|
-
gl.enableVertexAttribArray(posLoc);
|
|
7203
|
-
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
|
|
7204
|
-
const tex = gl.createTexture();
|
|
7205
|
-
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
7206
|
-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, GRID, GRID, 0, gl.RGBA, gl.FLOAT, null);
|
|
7207
|
-
const fbo = gl.createFramebuffer();
|
|
7208
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
|
7209
|
-
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
|
|
7210
|
-
gl.viewport(0, 0, GRID, GRID);
|
|
7211
|
-
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
7212
|
-
const out = new Float32Array(GRID * GRID * 4);
|
|
7213
|
-
gl.readPixels(0, 0, GRID, GRID, gl.RGBA, gl.FLOAT, out);
|
|
7214
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
7215
|
-
return out;
|
|
7216
|
-
}
|
|
7217
|
-
function runBiasCases(gl) {
|
|
7218
|
-
const results = [];
|
|
7219
|
-
for (const c of parityCases) {
|
|
7220
|
-
const strengths = c.stack.map((i) => getDeformer(i.type).glsl?.strength !== void 0);
|
|
7221
|
-
if (!strengths.every((h) => h === strengths[0])) continue;
|
|
7222
|
-
const scales = strengths[0];
|
|
7223
|
-
const at0 = runCaseOnGPU(gl, c, 0);
|
|
7224
|
-
const reference = scales ? null : runCaseOnGPU(gl, c, 1);
|
|
7225
|
-
let maxError = 0;
|
|
7226
|
-
for (let row = 0; row < GRID; row++) {
|
|
7227
|
-
for (let col = 0; col < GRID; col++) {
|
|
7228
|
-
const i4 = (row * GRID + col) * 4;
|
|
7229
|
-
const ex = reference ? reference[i4] : (col / (GRID - 1) - 0.5) * c.sheet.width;
|
|
7230
|
-
const ey = reference ? reference[i4 + 1] : (row / (GRID - 1) - 0.5) * c.sheet.height;
|
|
7231
|
-
const ez = reference ? reference[i4 + 2] : 0;
|
|
7232
|
-
maxError = Math.max(
|
|
7233
|
-
maxError,
|
|
7234
|
-
Math.abs(at0[i4] - ex),
|
|
7235
|
-
Math.abs(at0[i4 + 1] - ey),
|
|
7236
|
-
Math.abs(at0[i4 + 2] - ez)
|
|
7237
|
-
);
|
|
7238
|
-
}
|
|
7239
|
-
}
|
|
7240
|
-
results.push({
|
|
7241
|
-
name: `bias: ${c.name} \u2192 ${scales ? "flat at 0" : "ignores bias"}`,
|
|
7242
|
-
maxError,
|
|
7243
|
-
pass: maxError < PARITY_EPSILON
|
|
7244
|
-
});
|
|
7245
|
-
}
|
|
7246
|
-
return results;
|
|
7247
|
-
}
|
|
7248
|
-
function runParityHarness(canvas) {
|
|
7249
|
-
const cnv = canvas ?? document.createElement("canvas");
|
|
7250
|
-
const gl = cnv.getContext("webgl2");
|
|
7251
|
-
if (!gl) throw new Error("[paperlab parity] WebGL2 unavailable");
|
|
7252
|
-
if (!gl.getExtension("EXT_color_buffer_float")) {
|
|
7253
|
-
throw new Error("[paperlab parity] EXT_color_buffer_float unavailable");
|
|
7254
|
-
}
|
|
7255
|
-
const point = new THREE16.Vector3();
|
|
7256
|
-
const parity = parityCases.map((c) => {
|
|
7257
|
-
const gpu = runCaseOnGPU(gl, c);
|
|
7258
|
-
let maxError = 0;
|
|
7259
|
-
for (let row = 0; row < GRID; row++) {
|
|
7260
|
-
for (let col = 0; col < GRID; col++) {
|
|
7261
|
-
const u = col / (GRID - 1);
|
|
7262
|
-
const v = row / (GRID - 1);
|
|
7263
|
-
point.set((u - 0.5) * c.sheet.width, (v - 0.5) * c.sheet.height, 0);
|
|
7264
|
-
displacePoint(point, u, v, c.stack, { t: c.t, sheet: c.sheet });
|
|
7265
|
-
const i4 = (row * GRID + col) * 4;
|
|
7266
|
-
maxError = Math.max(
|
|
7267
|
-
maxError,
|
|
7268
|
-
Math.abs(gpu[i4] - point.x),
|
|
7269
|
-
Math.abs(gpu[i4 + 1] - point.y),
|
|
7270
|
-
Math.abs(gpu[i4 + 2] - point.z)
|
|
7271
|
-
);
|
|
7272
|
-
}
|
|
7273
|
-
}
|
|
7274
|
-
return { name: c.name, maxError, pass: maxError < PARITY_EPSILON };
|
|
7275
|
-
});
|
|
7276
|
-
return [...parity, ...runBiasCases(gl)];
|
|
7277
|
-
}
|
|
7278
|
-
|
|
7279
6854
|
// src/config/diff.ts
|
|
7280
6855
|
function isPlainObject(v) {
|
|
7281
6856
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
@@ -7378,40 +6953,218 @@ function describeConfig(config) {
|
|
|
7378
6953
|
`torn (deckled) ${config.surface.deckle.edges.join(" and ")} edge${config.surface.deckle.edges.length > 1 ? "s" : ""}`
|
|
7379
6954
|
);
|
|
7380
6955
|
}
|
|
7381
|
-
if ((config.surface.aging ?? 0) > 0.3) parts.push("visibly aged and yellowed");
|
|
7382
|
-
return parts.join(", ");
|
|
7383
|
-
}
|
|
7384
|
-
function componentName(config) {
|
|
7385
|
-
const raw = config.meta.name === "untitled" ? "PaperlabPaper" : config.meta.name;
|
|
7386
|
-
const pascal = raw.replace(/[^a-zA-Z0-9]+(.)/g, (_, c) => c.toUpperCase()).replace(/^./, (c) => c.toUpperCase()).replace(/[^a-zA-Z0-9]/g, "");
|
|
7387
|
-
return /^[A-Za-z]/.test(pascal) ? pascal : `Paper${pascal}`;
|
|
6956
|
+
if ((config.surface.aging ?? 0) > 0.3) parts.push("visibly aged and yellowed");
|
|
6957
|
+
return parts.join(", ");
|
|
6958
|
+
}
|
|
6959
|
+
function componentName(config) {
|
|
6960
|
+
const raw = config.meta.name === "untitled" ? "PaperlabPaper" : config.meta.name;
|
|
6961
|
+
const pascal = raw.replace(/[^a-zA-Z0-9]+(.)/g, (_, c) => c.toUpperCase()).replace(/^./, (c) => c.toUpperCase()).replace(/[^a-zA-Z0-9]/g, "");
|
|
6962
|
+
return /^[A-Za-z]/.test(pascal) ? pascal : `Paper${pascal}`;
|
|
6963
|
+
}
|
|
6964
|
+
function buildAgentPayload(config) {
|
|
6965
|
+
const name = componentName(config);
|
|
6966
|
+
const preset = JSON.stringify(diffConfig(config), null, 2);
|
|
6967
|
+
return `Integrate a Paperlab paper component into this project. (paperlab agent-payload v${AGENT_PAYLOAD_VERSION})
|
|
6968
|
+
|
|
6969
|
+
1. Install the dependencies:
|
|
6970
|
+
|
|
6971
|
+
npm i paperlab three @react-three/fiber gsap
|
|
6972
|
+
|
|
6973
|
+
2. Create the component below as \`components/${name}.tsx\` (or the project's
|
|
6974
|
+
component convention). It is self-contained and owns its own <Canvas>:
|
|
6975
|
+
|
|
6976
|
+
\`\`\`tsx
|
|
6977
|
+
import { Paper, type PaperConfigInput } from 'paperlab'
|
|
6978
|
+
|
|
6979
|
+
const preset = ${preset.replace(/\n/g, "\n")} satisfies PaperConfigInput
|
|
6980
|
+
|
|
6981
|
+
export function ${name}() {
|
|
6982
|
+
return <Paper preset={preset} />
|
|
6983
|
+
}
|
|
6984
|
+
\`\`\`
|
|
6985
|
+
|
|
6986
|
+
3. Sizing: the component fills its parent container. Place it where I ask;
|
|
6987
|
+
give the parent an explicit height.
|
|
6988
|
+
|
|
6989
|
+
4. Verify: run the dev server. You should see ${describeConfig(config)}.
|
|
6990
|
+
If the canvas is blank, the parent container almost certainly has no height \u2014
|
|
6991
|
+
give it one (this is the classic React Three Fiber integration bug, not a
|
|
6992
|
+
paperlab bug).
|
|
6993
|
+
|
|
6994
|
+
Constraints: don't modify the preset values; three >= 0.160 and React 19 are
|
|
6995
|
+
required; the component needs no props.`;
|
|
6996
|
+
}
|
|
6997
|
+
|
|
6998
|
+
// src/config/field-export.ts
|
|
6999
|
+
var MOTION_DEFAULTS = { driver: "autoplay", speed: 0.5 };
|
|
7000
|
+
var ENTRANCE_DEFAULTS = { type: "rise", stagger: 0.06, duration: 0.9 };
|
|
7001
|
+
function camel(name) {
|
|
7002
|
+
const id = name.replace(/[^a-zA-Z0-9]+(.)/g, (_, c) => c.toUpperCase()).replace(/[^a-zA-Z0-9]/g, "");
|
|
7003
|
+
const safe = /^[A-Za-z]/.test(id) ? id : `preset${id}`;
|
|
7004
|
+
return safe.charAt(0).toLowerCase() + safe.slice(1);
|
|
7005
|
+
}
|
|
7006
|
+
function distinctFieldPresets(input) {
|
|
7007
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
7008
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
7009
|
+
for (const paper of input.papers) {
|
|
7010
|
+
const key = JSON.stringify(paper.preset);
|
|
7011
|
+
if (byKey.has(key)) continue;
|
|
7012
|
+
let varName = camel(paper.presetName);
|
|
7013
|
+
while (usedNames.has(varName)) varName += "2";
|
|
7014
|
+
usedNames.add(varName);
|
|
7015
|
+
byKey.set(key, { varName, presetName: paper.presetName, preset: paper.preset });
|
|
7016
|
+
}
|
|
7017
|
+
return [...byKey.values()];
|
|
7018
|
+
}
|
|
7019
|
+
function nonDefault(value, defaults) {
|
|
7020
|
+
const out = {};
|
|
7021
|
+
for (const [k, v] of Object.entries(value ?? {})) {
|
|
7022
|
+
if (v !== void 0 && v !== defaults[k]) out[k] = v;
|
|
7023
|
+
}
|
|
7024
|
+
return out;
|
|
7025
|
+
}
|
|
7026
|
+
function diffFieldProps(input) {
|
|
7027
|
+
const out = { layout: input.layout };
|
|
7028
|
+
const layout = getLayout(input.layout);
|
|
7029
|
+
const layoutOptions = nonDefault(input.layoutOptions ?? {}, layout.defaults);
|
|
7030
|
+
if (Object.keys(layoutOptions).length > 0) out.layoutOptions = layoutOptions;
|
|
7031
|
+
const motion = nonDefault(input.motion, MOTION_DEFAULTS);
|
|
7032
|
+
if (Object.keys(motion).length > 0) out.motion = motion;
|
|
7033
|
+
const entrance = nonDefault(input.entrance, ENTRANCE_DEFAULTS);
|
|
7034
|
+
if (Object.keys(entrance).length > 0) out.entrance = entrance;
|
|
7035
|
+
return out;
|
|
7036
|
+
}
|
|
7037
|
+
var LAYOUT_PHRASES = {
|
|
7038
|
+
ring: "a ring you can see around",
|
|
7039
|
+
fan: "a fanned swatch deck hinged at one corner",
|
|
7040
|
+
spread: "a stack slid sideways, each sheet bowing further than the last",
|
|
7041
|
+
pile: "a heap on a desk, the sheets underneath pressed flat",
|
|
7042
|
+
wall: "a pinned studio wall of sheets, none hanging quite square",
|
|
7043
|
+
spill: "a dropped stack caught mid-air, each sheet bent its own way",
|
|
7044
|
+
sweep: "a specimen chart of one sheet at every stage of its deformation",
|
|
7045
|
+
book: "an open book, its pages splayed from a shared spine",
|
|
7046
|
+
accordion: "one continuous strip folded into a concertina",
|
|
7047
|
+
rack: "prints stood in a row and leaning back against a wall",
|
|
7048
|
+
sheet: "a stamp-block grid on a shared backing sheet"
|
|
7049
|
+
};
|
|
7050
|
+
function describeFieldConfig(input) {
|
|
7051
|
+
const n = input.papers.length;
|
|
7052
|
+
const stateful = input.papers.some((p) => p.preset.states || p.states);
|
|
7053
|
+
const zones = input.zones ?? [];
|
|
7054
|
+
const parts = [];
|
|
7055
|
+
if (input.layout === "sheet") {
|
|
7056
|
+
const rows = Number(input.layoutOptions?.rows ?? 2);
|
|
7057
|
+
const columns = Number(input.layoutOptions?.columns ?? 5);
|
|
7058
|
+
parts.push(`a ${rows}\xD7${columns} sheet of papers on a shared backing`);
|
|
7059
|
+
} else {
|
|
7060
|
+
const layoutPhrase = LAYOUT_PHRASES[input.layout] ?? `a ${input.layout} layout`;
|
|
7061
|
+
parts.push(`${n} papers arranged in ${layoutPhrase}`);
|
|
7062
|
+
}
|
|
7063
|
+
const driver = input.motion?.driver ?? "autoplay";
|
|
7064
|
+
if (stateful) {
|
|
7065
|
+
parts.push("hovering peels/reacts per paper (interaction states)");
|
|
7066
|
+
if (zones.length > 0) {
|
|
7067
|
+
parts.push(
|
|
7068
|
+
`dragging one past its tear threshold detaches it to carry to the ${zones.map((z26) => z26.id).join(" or ")} zone (release elsewhere flutters it back)`
|
|
7069
|
+
);
|
|
7070
|
+
}
|
|
7071
|
+
} else {
|
|
7072
|
+
if (driver === "autoplay") parts.push("slowly orbiting on their own");
|
|
7073
|
+
if (driver === "drag") parts.push("draggable sideways with the pointer");
|
|
7074
|
+
if (zones.length > 0) parts.push(`${zones.length} drop zone${zones.length > 1 ? "s" : ""}`);
|
|
7075
|
+
}
|
|
7076
|
+
const distinct = distinctFieldPresets(input);
|
|
7077
|
+
if (distinct.length > 1) {
|
|
7078
|
+
parts.push(`mixing ${distinct.length} paper presets (${distinct.map((d) => d.presetName).join(", ")})`);
|
|
7079
|
+
}
|
|
7080
|
+
return parts.join(", ");
|
|
7081
|
+
}
|
|
7082
|
+
function buildFieldComponentSource(input) {
|
|
7083
|
+
const distinct = distinctFieldPresets(input);
|
|
7084
|
+
const varByKey = new Map(distinct.map((d) => [JSON.stringify(d.preset), d.varName]));
|
|
7085
|
+
const consts = distinct.map(
|
|
7086
|
+
(d) => `const ${d.varName} = ${JSON.stringify(diffConfig(d.preset), null, 2)} satisfies PaperConfigInput`
|
|
7087
|
+
).join("\n\n");
|
|
7088
|
+
const slots = input.papers.map((paper) => {
|
|
7089
|
+
const varName = varByKey.get(JSON.stringify(paper.preset));
|
|
7090
|
+
const content = paper.content ? `, content: ${JSON.stringify(paper.content)}` : "";
|
|
7091
|
+
const states = paper.states ? `, states: ${JSON.stringify(paper.states)}` : "";
|
|
7092
|
+
return ` { preset: ${varName}${content}${states} },`;
|
|
7093
|
+
}).join("\n");
|
|
7094
|
+
const props = Object.entries(diffFieldProps(input)).map(
|
|
7095
|
+
([key, value]) => typeof value === "string" ? ` ${key}="${value}"` : ` ${key}={${JSON.stringify(value)}}`
|
|
7096
|
+
).join("\n");
|
|
7097
|
+
const zones = input.zones ?? [];
|
|
7098
|
+
const zoneImport = zones.length > 0 ? ", DropZone" : "";
|
|
7099
|
+
const zoneChildren = zones.map((zone) => {
|
|
7100
|
+
const accept = zone.accept ? ` accept={${JSON.stringify(zone.accept)}}` : "";
|
|
7101
|
+
const highlight = zone.highlight && zone.highlight !== "glow" ? ` highlight="${zone.highlight}"` : "";
|
|
7102
|
+
return ` <DropZone
|
|
7103
|
+
id="${zone.id}"${accept}
|
|
7104
|
+
bounds={${JSON.stringify(zone.bounds)}}${highlight}
|
|
7105
|
+
onPlace={(paper, zone) => {
|
|
7106
|
+
// First-run smoke test \u2014 you should see this in the console on drop:
|
|
7107
|
+
console.log(paper.presetName, '\u2192', zone.id)
|
|
7108
|
+
// Then replace it: stamp a postmark, advance the flow, persist the placement, \u2026
|
|
7109
|
+
}}
|
|
7110
|
+
/>`;
|
|
7111
|
+
}).join("\n");
|
|
7112
|
+
if (zones.length === 0) {
|
|
7113
|
+
return `import { PaperField, type FieldPaperSlot, type PaperConfigInput${zoneImport} } from 'paperlab'
|
|
7114
|
+
|
|
7115
|
+
${consts}
|
|
7116
|
+
|
|
7117
|
+
const papers: FieldPaperSlot[] = [
|
|
7118
|
+
${slots}
|
|
7119
|
+
]
|
|
7120
|
+
|
|
7121
|
+
export function PaperGallery() {
|
|
7122
|
+
return (
|
|
7123
|
+
<PaperField
|
|
7124
|
+
papers={papers}
|
|
7125
|
+
${props}
|
|
7126
|
+
/>
|
|
7127
|
+
)
|
|
7128
|
+
}`;
|
|
7129
|
+
}
|
|
7130
|
+
return `import { PaperField, type FieldPaperSlot, type PaperConfigInput${zoneImport} } from 'paperlab'
|
|
7131
|
+
|
|
7132
|
+
${consts}
|
|
7133
|
+
|
|
7134
|
+
const papers: FieldPaperSlot[] = [
|
|
7135
|
+
${slots}
|
|
7136
|
+
]
|
|
7137
|
+
|
|
7138
|
+
export function PaperGallery() {
|
|
7139
|
+
return (
|
|
7140
|
+
<PaperField
|
|
7141
|
+
papers={papers}
|
|
7142
|
+
${props}
|
|
7143
|
+
>
|
|
7144
|
+
${zoneChildren}
|
|
7145
|
+
</PaperField>
|
|
7146
|
+
)
|
|
7147
|
+
}`;
|
|
7388
7148
|
}
|
|
7389
|
-
function
|
|
7390
|
-
|
|
7391
|
-
const preset = JSON.stringify(diffConfig(config), null, 2);
|
|
7392
|
-
return `Integrate a Paperlab paper component into this project. (paperlab agent-payload v${AGENT_PAYLOAD_VERSION})
|
|
7149
|
+
function buildFieldAgentPayload(input) {
|
|
7150
|
+
return `Integrate a Paperlab paper gallery into this project. (paperlab agent-payload v${AGENT_PAYLOAD_VERSION})
|
|
7393
7151
|
|
|
7394
7152
|
1. Install the dependencies:
|
|
7395
7153
|
|
|
7396
7154
|
npm i paperlab three @react-three/fiber gsap
|
|
7397
7155
|
|
|
7398
|
-
2. Create the component below as \`components
|
|
7399
|
-
component convention). It is self-contained
|
|
7156
|
+
2. Create the component below as \`components/PaperGallery.tsx\` (or the
|
|
7157
|
+
project's component convention). It is self-contained \u2014 every preset it
|
|
7158
|
+
uses is inlined, and it owns its own <Canvas>:
|
|
7400
7159
|
|
|
7401
7160
|
\`\`\`tsx
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
const preset = ${preset.replace(/\n/g, "\n")} satisfies PaperConfigInput
|
|
7405
|
-
|
|
7406
|
-
export function ${name}() {
|
|
7407
|
-
return <Paper preset={preset} />
|
|
7408
|
-
}
|
|
7161
|
+
${buildFieldComponentSource(input)}
|
|
7409
7162
|
\`\`\`
|
|
7410
7163
|
|
|
7411
7164
|
3. Sizing: the component fills its parent container. Place it where I ask;
|
|
7412
7165
|
give the parent an explicit height.
|
|
7413
7166
|
|
|
7414
|
-
4. Verify: run the dev server. You should see ${
|
|
7167
|
+
4. Verify: run the dev server. You should see ${describeFieldConfig(input)}.
|
|
7415
7168
|
If the canvas is blank, the parent container almost certainly has no height \u2014
|
|
7416
7169
|
give it one (this is the classic React Three Fiber integration bug, not a
|
|
7417
7170
|
paperlab bug).
|
|
@@ -7420,382 +7173,374 @@ Constraints: don't modify the preset values; three >= 0.160 and React 19 are
|
|
|
7420
7173
|
required; the component needs no props.`;
|
|
7421
7174
|
}
|
|
7422
7175
|
|
|
7423
|
-
// src/
|
|
7424
|
-
var
|
|
7425
|
-
var
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
}
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
7176
|
+
// src/field/parity.ts
|
|
7177
|
+
var THREE16 = __toESM(require("three"), 1);
|
|
7178
|
+
var GRID = 48;
|
|
7179
|
+
var PARITY_EPSILON = 5e-4;
|
|
7180
|
+
var parityCases = [
|
|
7181
|
+
{
|
|
7182
|
+
name: "roll: defaults",
|
|
7183
|
+
stack: [{ type: "roll", options: { angle: 90, boundary: 0, radius: 0.12, spiral: 0.015 } }],
|
|
7184
|
+
sheet: { width: 1, height: 1.4 },
|
|
7185
|
+
t: 0
|
|
7186
|
+
},
|
|
7187
|
+
{
|
|
7188
|
+
name: "roll: tight receipt roll, rolling down",
|
|
7189
|
+
stack: [{ type: "roll", options: { angle: 270, boundary: -0.4, radius: 0.07, spiral: 0.02 } }],
|
|
7190
|
+
sheet: { width: 1, height: 2.6 },
|
|
7191
|
+
t: 0
|
|
7192
|
+
},
|
|
7193
|
+
{
|
|
7194
|
+
name: "curl: bottom-right peel",
|
|
7195
|
+
stack: [{ type: "curl", options: { corner: "bottom-right", amount: 0.45, radius: 0.2, skew: 0 } }],
|
|
7196
|
+
sheet: { width: 1.5, height: 1 },
|
|
7197
|
+
t: 0
|
|
7198
|
+
},
|
|
7199
|
+
{
|
|
7200
|
+
name: "curl: skewed top-left",
|
|
7201
|
+
stack: [{ type: "curl", options: { corner: "top-left", amount: 0.7, radius: 0.3, skew: 15 } }],
|
|
7202
|
+
sheet: { width: 1, height: 1 },
|
|
7203
|
+
t: 0
|
|
7204
|
+
},
|
|
7205
|
+
{
|
|
7206
|
+
name: "bend: positive arc at an angle",
|
|
7207
|
+
stack: [{ type: "bend", options: { curvature: 1.2, angle: 33 } }],
|
|
7208
|
+
sheet: { width: 1, height: 1.4 },
|
|
7209
|
+
t: 0
|
|
7210
|
+
},
|
|
7211
|
+
{
|
|
7212
|
+
/**
|
|
7213
|
+
* The gentle end, which nothing used to cover: the gate only ever tried
|
|
7214
|
+
* |curvature| ≥ 0.6, and `photo-print` — the field starter preset — bends
|
|
7215
|
+
* at 0.35. That band is where the arc's float32 evaluation loses its
|
|
7216
|
+
* cancellation, and where the two paths were 6e-4 apart until `bend` was
|
|
7217
|
+
* rewritten in cancellation-free form. Keep a case down here.
|
|
7218
|
+
*/
|
|
7219
|
+
name: "bend: barely-there arc (the band photo-print lives in)",
|
|
7220
|
+
stack: [{ type: "bend", options: { curvature: 0.35, angle: 0 } }],
|
|
7221
|
+
sheet: { width: 1.2, height: 0.9 },
|
|
7222
|
+
t: 0
|
|
7223
|
+
},
|
|
7224
|
+
{
|
|
7225
|
+
name: "bend: the gentlest arc the schema allows",
|
|
7226
|
+
stack: [{ type: "bend", options: { curvature: 0.02, angle: 61 } }],
|
|
7227
|
+
sheet: { width: 2, height: 2.6 },
|
|
7228
|
+
t: 0
|
|
7229
|
+
},
|
|
7230
|
+
{
|
|
7231
|
+
name: "bend: negative arc",
|
|
7232
|
+
stack: [{ type: "bend", options: { curvature: -0.8, angle: 0 } }],
|
|
7233
|
+
sheet: { width: 1, height: 1.4 },
|
|
7234
|
+
t: 0
|
|
7235
|
+
},
|
|
7236
|
+
{
|
|
7237
|
+
name: "fold: 90\xB0 hinge",
|
|
7238
|
+
stack: [{ type: "fold", options: { angle: 90, offset: 0, foldAngle: 90, radius: 0.06 } }],
|
|
7239
|
+
sheet: { width: 1, height: 1.4 },
|
|
7240
|
+
t: 0
|
|
7241
|
+
},
|
|
7242
|
+
{
|
|
7243
|
+
name: "fold: deep fold, travelling down",
|
|
7244
|
+
stack: [{ type: "fold", options: { angle: 270, offset: 0.2, foldAngle: 165, radius: 0.04 } }],
|
|
7245
|
+
sheet: { width: 1, height: 1.4 },
|
|
7246
|
+
t: 0
|
|
7247
|
+
},
|
|
7248
|
+
{
|
|
7249
|
+
name: "wave: free ripple at t=1.234",
|
|
7250
|
+
stack: [
|
|
7251
|
+
{
|
|
7252
|
+
type: "wave",
|
|
7253
|
+
options: { amplitude: 0.05, wavelength: 0.5, speed: 1, angle: 20, pinnedEdge: "none" }
|
|
7254
|
+
}
|
|
7255
|
+
],
|
|
7256
|
+
sheet: { width: 1, height: 1 },
|
|
7257
|
+
t: 1.234
|
|
7258
|
+
},
|
|
7259
|
+
{
|
|
7260
|
+
name: "wave: pinned top at t=2.5",
|
|
7261
|
+
stack: [
|
|
7262
|
+
{
|
|
7263
|
+
type: "wave",
|
|
7264
|
+
options: { amplitude: 0.04, wavelength: 0.4, speed: 1.5, angle: 80, pinnedEdge: "top" }
|
|
7265
|
+
}
|
|
7266
|
+
],
|
|
7267
|
+
sheet: { width: 1.2, height: 1.5 },
|
|
7268
|
+
t: 2.5
|
|
7269
|
+
},
|
|
7270
|
+
{
|
|
7271
|
+
name: "drape: banner hung from the top",
|
|
7272
|
+
stack: [
|
|
7273
|
+
{
|
|
7274
|
+
type: "drape",
|
|
7275
|
+
options: { amplitude: 0.17, folds: 5, falloff: 1.6, irregular: 0.45, gather: 0.5, pinnedEdge: "top" }
|
|
7276
|
+
}
|
|
7277
|
+
],
|
|
7278
|
+
sheet: { width: 1.5, height: 8.5 },
|
|
7279
|
+
t: 0
|
|
7280
|
+
},
|
|
7281
|
+
{
|
|
7282
|
+
name: "drape: deep irregular folds pinned at the bottom",
|
|
7283
|
+
stack: [
|
|
7284
|
+
{
|
|
7285
|
+
type: "drape",
|
|
7286
|
+
options: { amplitude: 0.55, folds: 11, falloff: 0.4, irregular: 1, gather: 1, pinnedEdge: "bottom" }
|
|
7287
|
+
}
|
|
7288
|
+
],
|
|
7289
|
+
sheet: { width: 2.2, height: 3 },
|
|
7290
|
+
t: 0
|
|
7291
|
+
},
|
|
7292
|
+
{
|
|
7293
|
+
name: "crumple: defaults",
|
|
7294
|
+
stack: [{ type: "crumple", options: { amount: 0.35, scale: 3, pull: 0.4, seed: 0 } }],
|
|
7295
|
+
sheet: { width: 1, height: 1.4 },
|
|
7296
|
+
t: 0
|
|
7297
|
+
},
|
|
7298
|
+
{
|
|
7299
|
+
// The adversarial one. `fract` is a sawtooth, so the two halves disagree
|
|
7300
|
+
// hardest where a crease lands exactly on a sample — a fine scale and a
|
|
7301
|
+
// seed whose fold directions are near-axis puts the most creases in
|
|
7302
|
+
// reach of the grid.
|
|
7303
|
+
name: "crumple: fully crushed, fine creases, off-axis seed",
|
|
7304
|
+
stack: [{ type: "crumple", options: { amount: 1, scale: 7.5, pull: 1, seed: 5 } }],
|
|
7305
|
+
sheet: { width: 1.3, height: 0.9 },
|
|
7306
|
+
t: 0
|
|
7307
|
+
},
|
|
7308
|
+
{
|
|
7309
|
+
// Crush then curl, which is the order the crumple BEHAVIOR stacks them:
|
|
7310
|
+
// the creases have to be placed on the flat sheet, not on a bent one.
|
|
7311
|
+
name: "stacked: crumple \u2218 bend (the crumple behavior)",
|
|
7312
|
+
stack: [
|
|
7313
|
+
{ type: "crumple", options: { amount: 0.62, scale: 3.3, pull: 0.5, seed: 2 } },
|
|
7314
|
+
{ type: "bend", options: { curvature: 0.31, angle: 35 } }
|
|
7315
|
+
],
|
|
7316
|
+
sheet: { width: 1.1, height: 1.4 },
|
|
7317
|
+
t: 0
|
|
7318
|
+
},
|
|
7319
|
+
{
|
|
7320
|
+
name: "stacked: letter-fold pair (fold \u2218 fold)",
|
|
7321
|
+
stack: [
|
|
7322
|
+
{ type: "fold", options: { angle: 270, offset: 0.2333, foldAngle: 120, radius: 0.05 } },
|
|
7323
|
+
{ type: "fold", options: { angle: 90, offset: 0.2333, foldAngle: 100, radius: 0.08 } }
|
|
7324
|
+
],
|
|
7325
|
+
sheet: { width: 1, height: 1.4 },
|
|
7326
|
+
t: 0
|
|
7327
|
+
},
|
|
7328
|
+
{
|
|
7329
|
+
name: "stacked: bend \u2218 roll \u2218 wave",
|
|
7330
|
+
stack: [
|
|
7331
|
+
{ type: "bend", options: { curvature: 0.6, angle: 0 } },
|
|
7332
|
+
{ type: "roll", options: { angle: 90, boundary: 0.1, radius: 0.15, spiral: 0 } },
|
|
7333
|
+
{
|
|
7334
|
+
type: "wave",
|
|
7335
|
+
options: { amplitude: 0.02, wavelength: 0.6, speed: 0.7, angle: 45, pinnedEdge: "none" }
|
|
7336
|
+
}
|
|
7337
|
+
],
|
|
7338
|
+
sheet: { width: 1, height: 1.4 },
|
|
7339
|
+
t: 0.8
|
|
7441
7340
|
}
|
|
7442
|
-
|
|
7341
|
+
];
|
|
7342
|
+
function buildParityFragment(stack, sheet2) {
|
|
7343
|
+
const composed = buildDisplacementGLSL(stack, sheet2);
|
|
7344
|
+
return (
|
|
7345
|
+
/* glsl */
|
|
7346
|
+
`#version 300 es
|
|
7347
|
+
precision highp float;
|
|
7348
|
+
uniform float uPlTime;
|
|
7349
|
+
uniform float uPlBias;
|
|
7350
|
+
${composed.functionsSrc}
|
|
7351
|
+
${composed.displaceSrc}
|
|
7352
|
+
out vec4 outColor;
|
|
7353
|
+
void main() {
|
|
7354
|
+
vec2 uv = (gl_FragCoord.xy - 0.5) / float(${GRID - 1});
|
|
7355
|
+
vec3 p = vec3((uv - 0.5) * uSheet, 0.0);
|
|
7356
|
+
outColor = vec4(plDisplace(p, uv, uPlTime, uPlBias), 1.0);
|
|
7357
|
+
}
|
|
7358
|
+
`
|
|
7359
|
+
);
|
|
7443
7360
|
}
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7361
|
+
var VERT = `#version 300 es
|
|
7362
|
+
in vec2 aPos;
|
|
7363
|
+
void main() { gl_Position = vec4(aPos, 0.0, 1.0); }
|
|
7364
|
+
`;
|
|
7365
|
+
function compile(gl, type, src) {
|
|
7366
|
+
const shader = gl.createShader(type);
|
|
7367
|
+
gl.shaderSource(shader, src);
|
|
7368
|
+
gl.compileShader(shader);
|
|
7369
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
7370
|
+
throw new Error(`[paperlab parity] shader compile failed: ${gl.getShaderInfoLog(shader)}`);
|
|
7448
7371
|
}
|
|
7449
|
-
return
|
|
7372
|
+
return shader;
|
|
7450
7373
|
}
|
|
7451
|
-
function
|
|
7452
|
-
const
|
|
7453
|
-
const
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
if (
|
|
7458
|
-
|
|
7459
|
-
|
|
7374
|
+
function runCaseOnGPU(gl, c, bias = 1) {
|
|
7375
|
+
const composed = buildDisplacementGLSL(c.stack, c.sheet);
|
|
7376
|
+
const program = gl.createProgram();
|
|
7377
|
+
gl.attachShader(program, compile(gl, gl.VERTEX_SHADER, VERT));
|
|
7378
|
+
gl.attachShader(program, compile(gl, gl.FRAGMENT_SHADER, buildParityFragment(c.stack, c.sheet)));
|
|
7379
|
+
gl.linkProgram(program);
|
|
7380
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
7381
|
+
throw new Error(`[paperlab parity] link failed: ${gl.getProgramInfoLog(program)}`);
|
|
7382
|
+
}
|
|
7383
|
+
gl.useProgram(program);
|
|
7384
|
+
for (const [name, value] of Object.entries(composed.uniforms)) {
|
|
7385
|
+
const loc = gl.getUniformLocation(program, name);
|
|
7386
|
+
if (!loc) continue;
|
|
7387
|
+
if (typeof value === "number") gl.uniform1f(loc, value);
|
|
7388
|
+
else if (value.length === 2) gl.uniform2f(loc, value[0], value[1]);
|
|
7389
|
+
else if (value.length === 3) gl.uniform3f(loc, value[0], value[1], value[2]);
|
|
7390
|
+
else gl.uniform4f(loc, value[0], value[1], value[2], value[3]);
|
|
7391
|
+
}
|
|
7392
|
+
const timeLoc = gl.getUniformLocation(program, "uPlTime");
|
|
7393
|
+
if (timeLoc) gl.uniform1f(timeLoc, c.t);
|
|
7394
|
+
const biasLoc = gl.getUniformLocation(program, "uPlBias");
|
|
7395
|
+
if (biasLoc) gl.uniform1f(biasLoc, bias);
|
|
7396
|
+
const quad = gl.createBuffer();
|
|
7397
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, quad);
|
|
7398
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
|
|
7399
|
+
const posLoc = gl.getAttribLocation(program, "aPos");
|
|
7400
|
+
gl.enableVertexAttribArray(posLoc);
|
|
7401
|
+
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
|
|
7402
|
+
const tex = gl.createTexture();
|
|
7403
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
7404
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, GRID, GRID, 0, gl.RGBA, gl.FLOAT, null);
|
|
7405
|
+
const fbo = gl.createFramebuffer();
|
|
7406
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
|
7407
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
|
|
7408
|
+
gl.viewport(0, 0, GRID, GRID);
|
|
7409
|
+
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
7410
|
+
const out = new Float32Array(GRID * GRID * 4);
|
|
7411
|
+
gl.readPixels(0, 0, GRID, GRID, gl.RGBA, gl.FLOAT, out);
|
|
7412
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
7460
7413
|
return out;
|
|
7461
7414
|
}
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
} else {
|
|
7485
|
-
const layoutPhrase = LAYOUT_PHRASES[input.layout] ?? `a ${input.layout} layout`;
|
|
7486
|
-
parts.push(`${n} papers arranged in ${layoutPhrase}`);
|
|
7487
|
-
}
|
|
7488
|
-
const driver = input.motion?.driver ?? "autoplay";
|
|
7489
|
-
if (stateful) {
|
|
7490
|
-
parts.push("hovering peels/reacts per paper (interaction states)");
|
|
7491
|
-
if (zones.length > 0) {
|
|
7492
|
-
parts.push(
|
|
7493
|
-
`dragging one past its tear threshold detaches it to carry to the ${zones.map((z26) => z26.id).join(" or ")} zone (release elsewhere flutters it back)`
|
|
7494
|
-
);
|
|
7415
|
+
function runBiasCases(gl) {
|
|
7416
|
+
const results = [];
|
|
7417
|
+
for (const c of parityCases) {
|
|
7418
|
+
const strengths = c.stack.map((i) => getDeformer(i.type).glsl?.strength !== void 0);
|
|
7419
|
+
if (!strengths.every((h) => h === strengths[0])) continue;
|
|
7420
|
+
const scales = strengths[0];
|
|
7421
|
+
const at0 = runCaseOnGPU(gl, c, 0);
|
|
7422
|
+
const reference = scales ? null : runCaseOnGPU(gl, c, 1);
|
|
7423
|
+
let maxError = 0;
|
|
7424
|
+
for (let row = 0; row < GRID; row++) {
|
|
7425
|
+
for (let col = 0; col < GRID; col++) {
|
|
7426
|
+
const i4 = (row * GRID + col) * 4;
|
|
7427
|
+
const ex = reference ? reference[i4] : (col / (GRID - 1) - 0.5) * c.sheet.width;
|
|
7428
|
+
const ey = reference ? reference[i4 + 1] : (row / (GRID - 1) - 0.5) * c.sheet.height;
|
|
7429
|
+
const ez = reference ? reference[i4 + 2] : 0;
|
|
7430
|
+
maxError = Math.max(
|
|
7431
|
+
maxError,
|
|
7432
|
+
Math.abs(at0[i4] - ex),
|
|
7433
|
+
Math.abs(at0[i4 + 1] - ey),
|
|
7434
|
+
Math.abs(at0[i4 + 2] - ez)
|
|
7435
|
+
);
|
|
7436
|
+
}
|
|
7495
7437
|
}
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
const distinct = distinctFieldPresets(input);
|
|
7502
|
-
if (distinct.length > 1) {
|
|
7503
|
-
parts.push(`mixing ${distinct.length} paper presets (${distinct.map((d) => d.presetName).join(", ")})`);
|
|
7438
|
+
results.push({
|
|
7439
|
+
name: `bias: ${c.name} \u2192 ${scales ? "flat at 0" : "ignores bias"}`,
|
|
7440
|
+
maxError,
|
|
7441
|
+
pass: maxError < PARITY_EPSILON
|
|
7442
|
+
});
|
|
7504
7443
|
}
|
|
7505
|
-
return
|
|
7444
|
+
return results;
|
|
7506
7445
|
}
|
|
7507
|
-
function
|
|
7508
|
-
const
|
|
7509
|
-
const
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
const slots = input.papers.map((paper) => {
|
|
7514
|
-
const varName = varByKey.get(JSON.stringify(paper.preset));
|
|
7515
|
-
const content = paper.content ? `, content: ${JSON.stringify(paper.content)}` : "";
|
|
7516
|
-
const states = paper.states ? `, states: ${JSON.stringify(paper.states)}` : "";
|
|
7517
|
-
return ` { preset: ${varName}${content}${states} },`;
|
|
7518
|
-
}).join("\n");
|
|
7519
|
-
const props = Object.entries(diffFieldProps(input)).map(
|
|
7520
|
-
([key, value]) => typeof value === "string" ? ` ${key}="${value}"` : ` ${key}={${JSON.stringify(value)}}`
|
|
7521
|
-
).join("\n");
|
|
7522
|
-
const zones = input.zones ?? [];
|
|
7523
|
-
const zoneImport = zones.length > 0 ? ", DropZone" : "";
|
|
7524
|
-
const zoneChildren = zones.map((zone) => {
|
|
7525
|
-
const accept = zone.accept ? ` accept={${JSON.stringify(zone.accept)}}` : "";
|
|
7526
|
-
const highlight = zone.highlight && zone.highlight !== "glow" ? ` highlight="${zone.highlight}"` : "";
|
|
7527
|
-
return ` <DropZone
|
|
7528
|
-
id="${zone.id}"${accept}
|
|
7529
|
-
bounds={${JSON.stringify(zone.bounds)}}${highlight}
|
|
7530
|
-
onPlace={(paper, zone) => {
|
|
7531
|
-
// First-run smoke test \u2014 you should see this in the console on drop:
|
|
7532
|
-
console.log(paper.presetName, '\u2192', zone.id)
|
|
7533
|
-
// Then replace it: stamp a postmark, advance the flow, persist the placement, \u2026
|
|
7534
|
-
}}
|
|
7535
|
-
/>`;
|
|
7536
|
-
}).join("\n");
|
|
7537
|
-
if (zones.length === 0) {
|
|
7538
|
-
return `import { PaperField, type FieldPaperSlot, type PaperConfigInput${zoneImport} } from 'paperlab'
|
|
7539
|
-
|
|
7540
|
-
${consts}
|
|
7541
|
-
|
|
7542
|
-
const papers: FieldPaperSlot[] = [
|
|
7543
|
-
${slots}
|
|
7544
|
-
]
|
|
7545
|
-
|
|
7546
|
-
export function PaperGallery() {
|
|
7547
|
-
return (
|
|
7548
|
-
<PaperField
|
|
7549
|
-
papers={papers}
|
|
7550
|
-
${props}
|
|
7551
|
-
/>
|
|
7552
|
-
)
|
|
7553
|
-
}`;
|
|
7446
|
+
function runParityHarness(canvas) {
|
|
7447
|
+
const cnv = canvas ?? document.createElement("canvas");
|
|
7448
|
+
const gl = cnv.getContext("webgl2");
|
|
7449
|
+
if (!gl) throw new Error("[paperlab parity] WebGL2 unavailable");
|
|
7450
|
+
if (!gl.getExtension("EXT_color_buffer_float")) {
|
|
7451
|
+
throw new Error("[paperlab parity] EXT_color_buffer_float unavailable");
|
|
7554
7452
|
}
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
}
|
|
7573
|
-
}
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
1. Install the dependencies:
|
|
7578
|
-
|
|
7579
|
-
npm i paperlab three @react-three/fiber gsap
|
|
7580
|
-
|
|
7581
|
-
2. Create the component below as \`components/PaperGallery.tsx\` (or the
|
|
7582
|
-
project's component convention). It is self-contained \u2014 every preset it
|
|
7583
|
-
uses is inlined, and it owns its own <Canvas>:
|
|
7584
|
-
|
|
7585
|
-
\`\`\`tsx
|
|
7586
|
-
${buildFieldComponentSource(input)}
|
|
7587
|
-
\`\`\`
|
|
7588
|
-
|
|
7589
|
-
3. Sizing: the component fills its parent container. Place it where I ask;
|
|
7590
|
-
give the parent an explicit height.
|
|
7591
|
-
|
|
7592
|
-
4. Verify: run the dev server. You should see ${describeFieldConfig(input)}.
|
|
7593
|
-
If the canvas is blank, the parent container almost certainly has no height \u2014
|
|
7594
|
-
give it one (this is the classic React Three Fiber integration bug, not a
|
|
7595
|
-
paperlab bug).
|
|
7596
|
-
|
|
7597
|
-
Constraints: don't modify the preset values; three >= 0.160 and React 19 are
|
|
7598
|
-
required; the component needs no props.`;
|
|
7599
|
-
}
|
|
7600
|
-
|
|
7601
|
-
// src/core/modes.ts
|
|
7602
|
-
function resolveMode(requested, ctx) {
|
|
7603
|
-
if (requested !== "auto") return requested;
|
|
7604
|
-
if (ctx.interactive || ctx.physics !== "none" || ctx.count <= 10) return "hero";
|
|
7605
|
-
return "field";
|
|
7453
|
+
const point = new THREE16.Vector3();
|
|
7454
|
+
const parity = parityCases.map((c) => {
|
|
7455
|
+
const gpu = runCaseOnGPU(gl, c);
|
|
7456
|
+
let maxError = 0;
|
|
7457
|
+
for (let row = 0; row < GRID; row++) {
|
|
7458
|
+
for (let col = 0; col < GRID; col++) {
|
|
7459
|
+
const u = col / (GRID - 1);
|
|
7460
|
+
const v = row / (GRID - 1);
|
|
7461
|
+
point.set((u - 0.5) * c.sheet.width, (v - 0.5) * c.sheet.height, 0);
|
|
7462
|
+
displacePoint(point, u, v, c.stack, { t: c.t, sheet: c.sheet });
|
|
7463
|
+
const i4 = (row * GRID + col) * 4;
|
|
7464
|
+
maxError = Math.max(
|
|
7465
|
+
maxError,
|
|
7466
|
+
Math.abs(gpu[i4] - point.x),
|
|
7467
|
+
Math.abs(gpu[i4 + 1] - point.y),
|
|
7468
|
+
Math.abs(gpu[i4 + 2] - point.z)
|
|
7469
|
+
);
|
|
7470
|
+
}
|
|
7471
|
+
}
|
|
7472
|
+
return { name: c.name, maxError, pass: maxError < PARITY_EPSILON };
|
|
7473
|
+
});
|
|
7474
|
+
return [...parity, ...runBiasCases(gl)];
|
|
7606
7475
|
}
|
|
7607
7476
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7608
7477
|
0 && (module.exports = {
|
|
7609
|
-
AGENT_PAYLOAD_VERSION,
|
|
7610
7478
|
AUTO_CEILING,
|
|
7611
|
-
ClothSim,
|
|
7612
7479
|
DropZone,
|
|
7613
|
-
DropZoneRegistry,
|
|
7614
7480
|
FLAT_SEGMENTS,
|
|
7615
7481
|
LightRig,
|
|
7616
|
-
ON_TWOS_FPS,
|
|
7617
7482
|
PARITY_EPSILON,
|
|
7618
7483
|
Paper,
|
|
7619
|
-
PaperFallback,
|
|
7620
7484
|
PaperField,
|
|
7621
7485
|
PaperFieldMesh,
|
|
7622
7486
|
PaperLighting,
|
|
7623
|
-
PaperMaterial,
|
|
7624
7487
|
PaperMesh,
|
|
7625
|
-
PaperMirror,
|
|
7626
|
-
PaperStateMachine,
|
|
7627
7488
|
SAG_TOL,
|
|
7628
|
-
SHEET_LIFT,
|
|
7629
|
-
TRANSMISSION_GAIN,
|
|
7630
|
-
accordion,
|
|
7631
|
-
applyDeformerStack,
|
|
7632
|
-
atlasGrid,
|
|
7633
|
-
backContentSchema,
|
|
7634
|
-
barcodeBars,
|
|
7635
7489
|
behaviorConfigSchema,
|
|
7636
|
-
bend,
|
|
7637
|
-
bendOptionsSchema,
|
|
7638
|
-
book,
|
|
7639
7490
|
buildAgentPayload,
|
|
7640
|
-
buildDisplacementGLSL,
|
|
7641
7491
|
buildFieldAgentPayload,
|
|
7642
7492
|
buildFieldComponentSource,
|
|
7643
|
-
buildFieldFragmentShader,
|
|
7644
|
-
buildFieldVertexShader,
|
|
7645
7493
|
buildJsxSnippet,
|
|
7646
|
-
cardContentSchema,
|
|
7647
|
-
carry,
|
|
7648
|
-
carryDrive,
|
|
7649
|
-
carryOptionsSchema,
|
|
7650
7494
|
clothConfigSchema,
|
|
7651
|
-
colonnade,
|
|
7652
|
-
composeSurface,
|
|
7653
|
-
contentSchema,
|
|
7654
|
-
contentText,
|
|
7655
7495
|
coreStateNames,
|
|
7656
|
-
cornerNames,
|
|
7657
|
-
createSheetGeometry,
|
|
7658
|
-
crumple,
|
|
7659
|
-
crumpleBehavior,
|
|
7660
|
-
crumpleBehaviorOptionsSchema,
|
|
7661
|
-
crumpleOptionsSchema,
|
|
7662
|
-
curl,
|
|
7663
|
-
curlOptionsSchema,
|
|
7664
|
-
dampTo,
|
|
7665
|
-
deformerInstanceSchema,
|
|
7666
7496
|
describeConfig,
|
|
7667
7497
|
describeFieldConfig,
|
|
7668
7498
|
diffConfig,
|
|
7669
7499
|
diffFieldProps,
|
|
7670
|
-
displacePoint,
|
|
7671
|
-
distinctFieldPresets,
|
|
7672
|
-
drape,
|
|
7673
|
-
drapeOptionsSchema,
|
|
7674
|
-
drawBacking,
|
|
7675
|
-
fall,
|
|
7676
|
-
fallOptionsSchema,
|
|
7677
|
-
fan,
|
|
7678
|
-
fieldBounds,
|
|
7679
|
-
fieldKeyboardStep,
|
|
7680
|
-
filmNames,
|
|
7681
|
-
fitCamera,
|
|
7682
|
-
flattenNumeric,
|
|
7683
|
-
flight,
|
|
7684
|
-
flightOptionsSchema,
|
|
7685
|
-
flightPose,
|
|
7686
|
-
flip,
|
|
7687
|
-
flipOptionsSchema,
|
|
7688
|
-
fly,
|
|
7689
|
-
flyOptionsSchema,
|
|
7690
|
-
fold,
|
|
7691
|
-
foldOptionsSchema,
|
|
7692
7500
|
getBehavior,
|
|
7693
7501
|
getDeformer,
|
|
7694
|
-
getIdlePreset,
|
|
7695
7502
|
getLayout,
|
|
7696
|
-
getLightingPreset,
|
|
7697
7503
|
getPreset,
|
|
7698
7504
|
getStock,
|
|
7699
|
-
groupFieldPapers,
|
|
7700
|
-
gust,
|
|
7701
|
-
hang,
|
|
7702
|
-
hangOptionsSchema,
|
|
7703
7505
|
idleNames,
|
|
7704
|
-
idlePresets,
|
|
7705
7506
|
isBuiltinPreset,
|
|
7706
|
-
letterFold,
|
|
7707
|
-
letterFoldOptionsSchema,
|
|
7708
7507
|
lightAngles,
|
|
7709
|
-
lightPosition,
|
|
7710
7508
|
lightSchema,
|
|
7711
|
-
lightenHex,
|
|
7712
7509
|
lightingNames,
|
|
7713
|
-
lightingPresets,
|
|
7714
7510
|
listBehaviors,
|
|
7715
7511
|
listDeformers,
|
|
7716
7512
|
listLayouts,
|
|
7717
7513
|
listPresets,
|
|
7718
|
-
makeGoboTexture,
|
|
7719
7514
|
mergeConfig,
|
|
7720
7515
|
mergeWithDeletes,
|
|
7721
|
-
outwardCorner,
|
|
7722
7516
|
paperConfigSchema,
|
|
7723
7517
|
paperEdges,
|
|
7724
7518
|
paperStatesSchema,
|
|
7725
7519
|
parityCases,
|
|
7726
7520
|
parsePreset,
|
|
7727
|
-
peel,
|
|
7728
|
-
peelOptionsSchema,
|
|
7729
7521
|
physicsNames,
|
|
7730
|
-
physicsSchema,
|
|
7731
|
-
pile,
|
|
7732
|
-
quantizeProgress,
|
|
7733
7522
|
quantizeSegments,
|
|
7734
|
-
quantizeTime,
|
|
7735
|
-
rack,
|
|
7736
|
-
receiptContentSchema,
|
|
7737
|
-
receiptTotals,
|
|
7738
7523
|
recordStateOverride,
|
|
7739
7524
|
registerBehavior,
|
|
7740
7525
|
registerDeformer,
|
|
7741
7526
|
registerLayout,
|
|
7742
7527
|
registerPreset,
|
|
7743
|
-
resolveConfig,
|
|
7744
|
-
resolveDeformerStack,
|
|
7745
|
-
resolveFieldSlotConfig,
|
|
7746
7528
|
resolveLighting,
|
|
7747
|
-
resolveMode,
|
|
7748
|
-
resolveSegments,
|
|
7749
7529
|
resolveStateConfig,
|
|
7750
|
-
ribbon,
|
|
7751
|
-
ribbonOptionsSchema,
|
|
7752
|
-
ring,
|
|
7753
|
-
roll,
|
|
7754
|
-
rollOptionsSchema,
|
|
7755
7530
|
runParityHarness,
|
|
7756
|
-
sceneSchema,
|
|
7757
7531
|
segmentsForArc,
|
|
7758
7532
|
segmentsForSine,
|
|
7759
7533
|
serializePreset,
|
|
7760
|
-
settle,
|
|
7761
|
-
settleOptionsSchema,
|
|
7762
|
-
sheet,
|
|
7763
|
-
sheetBackingSize,
|
|
7764
7534
|
sheetLayoutSchema,
|
|
7765
|
-
sheetSchema,
|
|
7766
|
-
sheetSlotXY,
|
|
7767
|
-
silhouetteRects,
|
|
7768
7535
|
spanAlong,
|
|
7769
|
-
spill,
|
|
7770
|
-
spread,
|
|
7771
|
-
stackAutoSegments,
|
|
7772
|
-
stackMinSegments,
|
|
7773
|
-
stackUniformValues,
|
|
7774
7536
|
stateDefSchema,
|
|
7775
|
-
stateEventTransitions,
|
|
7776
|
-
stateTransitionSchema,
|
|
7777
7537
|
stockNames,
|
|
7778
|
-
stockSchema,
|
|
7779
7538
|
stocks,
|
|
7780
|
-
stripStates,
|
|
7781
7539
|
supportsWebGL,
|
|
7782
|
-
surfaceSchema,
|
|
7783
|
-
sweep,
|
|
7784
|
-
tornEdgesOnDetach,
|
|
7785
|
-
translucencyUniforms,
|
|
7786
|
-
translucencyValues,
|
|
7787
7540
|
uniquePresetName,
|
|
7788
7541
|
unregisterPreset,
|
|
7789
|
-
unroll,
|
|
7790
|
-
unrollOptionsSchema,
|
|
7791
|
-
useContentAtlas,
|
|
7792
7542
|
usePaperStates,
|
|
7793
7543
|
usePrefersReducedMotion,
|
|
7794
|
-
|
|
7795
|
-
wave,
|
|
7796
|
-
waveOptionsSchema,
|
|
7797
|
-
withSheetCellFromPaper,
|
|
7798
|
-
wrapLines,
|
|
7799
|
-
zoneAccepts
|
|
7544
|
+
wrapLines
|
|
7800
7545
|
});
|
|
7801
7546
|
//# sourceMappingURL=index.cjs.map
|