'use strict'; const { Document } = require('/document.js'); const { AddChildNodesCommandBuilder, NodeChildType, InsertionMode, DocumentCommand } = require('/commands.js'); const { Dialog, DialogResult } = require('/dialog.js'); const { app } = require('/application.js'); const { PinchPunchFilterRasterNodeDefinition, TwirlFilterRasterNodeDefinition, RippleFilterRasterNodeDefinition, SphericalFilterRasterNodeDefinition, } = require('/nodes.js'); function abort(msg, alert = true) { if (alert) app.alert(msg); throw new Error(msg); } const doc = Document.current; if (!doc) abort('No active document.'); const spread = doc.currentSpread; // Capture layer BEFORE dialog let targetLayer = null; for (const node of doc.selection.nodes) { targetLayer = node; break; } if (!targetLayer) targetLayer = spread.firstChild; if (!targetLayer) abort('No layer found.'); const bbox = targetLayer.getSpreadBaseBox(false); const { x, y, width, height } = bbox; const minDim = Math.min(width, height); // ---- Dialog ---- const dlg = Dialog.create("Random Distort Effects"); dlg.initialWidth = 200; const col = dlg.addColumn(); const g1 = col.addGroup(""); const countEditor = g1.addUnitValueEditor("Number of effects", 'none', 'none', 15, 1, 100); countEditor.precision = 0; countEditor.isFullWidth = true; const scaleEditor = g1.addUnitValueEditor("Parameter scale (%)", 'none', 'none', 100, 2, 500); scaleEditor.precision = 0; scaleEditor.isFullWidth = true; const chkPinch = g1.addCheckBox("Pinch / Punch", true); const chkTwirl = g1.addCheckBox("Twirl", true); const chkRipple = g1.addCheckBox("Ripple", true); const chkSpheral = g1.addCheckBox("Spherical", true); const dlgResult = dlg.runModal(); if (dlgResult.value !== DialogResult.Ok.value) abort('Cancelled.', false); // Re-set spread AFTER dialog (dialog resets spread context) doc.executeCommand(DocumentCommand.createSetCurrentSpread(spread)); const effectCount = Math.max(1, Math.min(50, Math.round(countEditor.value))); const scalingFactor = Math.max(0.1, scaleEditor.value / 100); const activeTypes = []; if (chkPinch.value) activeTypes.push('pinchPunch'); if (chkTwirl.value) activeTypes.push('twirl'); if (chkRipple.value) activeTypes.push('ripple'); if (chkSpheral.value) activeTypes.push('spherical'); if (activeTypes.length === 0) abort('No effect types selected.'); function rnd(min, max) { return min + Math.random() * (max - min); } function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); } function rndPos() { return { x: x + rnd(-0.1*width, 1.1*width), y: y + rnd(-0.1*height, 1.1*height) }; } function rndRadius(minPct, maxPct) { return Math.max(10, minDim * rnd(minPct, maxPct)); } // Filter definitions need a typed parameters object, not a plain {} function setParams(def, values) { const p = def.parameters; for (const k of Object.keys(values)) p[k] = values[k]; def.parameters = p; } const builder = AddChildNodesCommandBuilder.create(); builder.setInsertionTarget(targetLayer); builder.setInsertionMode(InsertionMode.InsertAsFirstChild); builder.clearCurrentSelection = false; builder.appendCurrentSelection = false; // Scaling can push values past what each filter accepts, so clamp to the legal ranges for (let i = 0; i < effectCount; i++) { const type = activeTypes[Math.floor(Math.random() * activeTypes.length)]; switch (type) { case 'pinchPunch': { const def = PinchPunchFilterRasterNodeDefinition.createDefault(doc); setParams(def, { intensity: clamp(rnd(-100, 100) * scalingFactor, -100, 100), radius: clamp(rndRadius(0.05, 0.35) * scalingFactor, 1, 1024), position: rndPos() }); builder.addPinchPunchFilterRasterNode(def); break; } case 'twirl': { const def = TwirlFilterRasterNodeDefinition.createDefault(doc); setParams(def, { angle: clamp(rnd(-Math.PI * 4, Math.PI * 4) * scalingFactor, -Math.PI * 4, Math.PI * 4), radius: clamp(rndRadius(0.05, 0.4) * scalingFactor, 1, 1024), position: rndPos() }); builder.addTwirlFilterRasterNode(def); break; } case 'ripple': { const def = RippleFilterRasterNodeDefinition.createDefault(doc); setParams(def, { intensity: clamp(rnd(10, 300) * scalingFactor, 0, 500), position: rndPos() }); builder.addRippleFilterRasterNode(def); break; } case 'spherical': { const def = SphericalFilterRasterNodeDefinition.createDefault(doc); setParams(def, { intensity: clamp(rnd(-1, 1) * scalingFactor, -1, 1), radius: clamp(rndRadius(0.05, 0.4) * scalingFactor, 1, 1024), position: rndPos() }); builder.addSphericalFilterRasterNode(def); break; } } } const cmd = builder.createCommand(false, NodeChildType.Enclosure); doc.executeCommand(cmd); let enc = 0; for (const c of targetLayer.enclosures) enc++; app.alert(`Done! Applied ${effectCount} filters at ${Math.round(scalingFactor * 100)}% intensity. (${enc} total on layer)`);