jspaint/src/simulate-random-gestures.js

270 lines
8.2 KiB
JavaScript

((exports) => {
let seed = 4; // chosen later
const seededRandom = (max = 1, min = 0) => {
seed = (seed * 9301 + 49297) % 233280;
const rnd = seed / 233280;
return min + rnd * (max - min);
};
exports.stopSimulatingGestures && exports.stopSimulatingGestures();
exports.simulatingGestures = false;
let gestureTimeoutID;
let periodicGesturesTimeoutID;
let choose = (array) => array[~~(seededRandom() * array.length)];
let isAnyMenuOpen = () => $(".menu-button.active").length > 0;
let cursor_image = new Image();
cursor_image.src = "images/cursors/default.png";
const $cursor = $(cursor_image).addClass("user-cursor");
$cursor.css({
position: "absolute",
left: 0,
top: 0,
opacity: 0,
zIndex: 5, // @#: z-index
pointerEvents: "none",
transition: "opacity 0.5s",
});
exports.simulateRandomGesture = (callback, { shift, shiftToggleChance = 0.01, secondary, secondaryToggleChance, target = main_canvas }) => {
let startWithinRect = target.getBoundingClientRect();
let canvasAreaRect = $canvas_area[0].getBoundingClientRect();
let startMinX = Math.max(startWithinRect.left, canvasAreaRect.left);
let startMaxX = Math.min(startWithinRect.right, canvasAreaRect.right);
let startMinY = Math.max(startWithinRect.top, canvasAreaRect.top);
let startMaxY = Math.min(startWithinRect.bottom, canvasAreaRect.bottom);
let startPointX = startMinX + seededRandom() * (startMaxX - startMinX);
let startPointY = startMinY + seededRandom() * (startMaxY - startMinY);
$cursor.appendTo($app);
let triggerMouseEvent = (type, point) => {
if (isAnyMenuOpen()) {
return;
}
const clientX = point.x;
const clientY = point.y;
const el_over = document.elementFromPoint(clientX, clientY);
const do_nothing = !type.match(/move/) && (!el_over || !el_over.closest(".canvas-area"));
$cursor.css({
display: "block",
position: "absolute",
left: clientX,
top: clientY,
opacity: do_nothing ? 0.5 : 1,
});
if (do_nothing) {
return;
}
let event = new $.Event(type, {
view: window,
bubbles: true,
cancelable: true,
clientX,
clientY,
screenX: clientX,
screenY: clientY,
offsetX: point.x,
offsetY: point.y,
button: secondary ? 2 : 0,
buttons: secondary ? 2 : 1,
shiftKey: shift,
});
$(target).trigger(event);
};
let t = 0;
let gestureComponents = [];
let numberOfComponents = 5;
for (let i = 0; i < numberOfComponents; i += 1) {
gestureComponents.push({
rx:
(seededRandom() * Math.min(canvasAreaRect.width, canvasAreaRect.height)) /
2 /
numberOfComponents,
ry:
(seededRandom() * Math.min(canvasAreaRect.width, canvasAreaRect.height)) /
2 /
numberOfComponents,
angularFactor: seededRandom() * 5 - seededRandom(),
angularOffset: seededRandom() * 5 - seededRandom(),
});
}
const stepsInGesture = 50;
let pointForTimeWithArbitraryStart = (t) => {
let point = { x: 0, y: 0 };
for (let i = 0; i < gestureComponents.length; i += 1) {
let { rx, ry, angularFactor, angularOffset } = gestureComponents[i];
point.x +=
Math.sin(Math.PI * 2 * ((t / 2) * angularFactor + angularOffset)) *
rx;
point.y +=
Math.cos(Math.PI * 2 * ((t / 2) * angularFactor + angularOffset)) *
ry;
}
return point;
};
let pointForTime = (t) => {
let point = pointForTimeWithArbitraryStart(t);
let zeroPoint = pointForTimeWithArbitraryStart(0);
point.x -= zeroPoint.x;
point.y -= zeroPoint.y;
point.x += startPointX;
point.y += startPointY;
return point;
};
triggerMouseEvent("pointerenter", pointForTime(t)); // so dynamic cursors follow the simulation cursor
triggerMouseEvent("pointerdown", pointForTime(t));
let move = () => {
t += 1 / stepsInGesture;
if (seededRandom() < shiftToggleChance) {
shift = !shift;
}
if (seededRandom() < secondaryToggleChance) {
secondary = !secondary;
}
if (t > 1) {
triggerMouseEvent("pointerup", pointForTime(t));
$cursor.remove();
if (callback) {
callback();
}
} else {
triggerMouseEvent("pointermove", pointForTime(t));
gestureTimeoutID = setTimeout(move, 10);
}
};
triggerMouseEvent("pointerleave", pointForTime(t));
move();
};
exports.simulateRandomGesturesPeriodically = () => {
exports.simulatingGestures = true;
if (window.drawRandomlySeed != null) {
seed = window.drawRandomlySeed;
} else {
seed = ~~(Math.random() * 5000000);
}
window.console && console.log("Using seed:", seed);
window.console && console.log("Note: Seeds are not guaranteed to work with different versions of the app, but within the same version it should produce the same results given the same starting document & other state & NO interference... except for airbrush randomness");
window.console && console.log(`To use this seed:
window.drawRandomlySeed = ${seed};
document.body.style.width = "${getComputedStyle(document.body).width}";
document.body.style.height = "${getComputedStyle(document.body).height}";
simulateRandomGesturesPeriodically();
delete window.drawRandomlySeed;
`);
let delayBetweenGestures = 500;
let shiftStart = false;
let shiftStartToggleChance = 0.1;
let shiftToggleChance = 0.001;
let secondaryStart = false;
let secondaryStartToggleChance = 0.1;
let secondaryToggleChance = 0.001;
let switchToolsChance = 0.5;
let multiToolsChance = 0.8;
let pickColorChance = 0.5;
let pickToolOptionsChance = 0.8;
let scrollChance = 0.2;
let dragSelectionChance = 0.8;
// scroll randomly absolutely initially so the starting scroll doesn't play into whether a seed reproduces
$canvas_area.scrollTop($canvas_area.width() * seededRandom());
$canvas_area.scrollLeft($canvas_area.height() * seededRandom());
let _simulateRandomGesture = (callback) => {
exports.simulateRandomGesture(callback, {
shift: shiftStart,
shiftToggleChance,
secondary: secondaryStart,
secondaryToggleChance
});
};
let waitThenGo = () => {
// @TODO: a button to stop it as well (maybe make "stop drawing randomly" a link button?)
$status_text.text("Press Esc to stop drawing randomly.");
if (isAnyMenuOpen()) {
periodicGesturesTimeoutID = setTimeout(waitThenGo, 50);
return;
}
if (seededRandom() < shiftStartToggleChance) {
shiftStart = !shiftStart;
}
if (seededRandom() < secondaryStartToggleChance) {
secondaryStart = !secondaryStart;
}
if (seededRandom() < switchToolsChance) {
let multiToolsPlz = seededRandom() < multiToolsChance;
$(choose($(".tool, tool-button"))).trigger($.Event("click", { shiftKey: multiToolsPlz }));
}
if (seededRandom() < pickToolOptionsChance) {
$(choose($(".tool-options *"))).trigger("click");
}
if (seededRandom() < pickColorChance) {
// @TODO: maybe these should respond to a normal click?
let secondary = seededRandom() < 0.5;
const colorButton = choose($(".swatch, .color-button"));
$(colorButton)
.trigger($.Event("pointerdown", { button: secondary ? 2 : 0 }))
.trigger($.Event("click", { button: secondary ? 2 : 0 }))
.trigger($.Event("pointerup", { button: secondary ? 2 : 0 }));
}
if (seededRandom() < scrollChance) {
let scrollAmount = (seededRandom() * 2 - 1) * 700;
if (seededRandom() < 0.5) {
$canvas_area.scrollTop($canvas_area.scrollTop() + scrollAmount);
} else {
$canvas_area.scrollLeft($canvas_area.scrollLeft() + scrollAmount);
}
}
periodicGesturesTimeoutID = setTimeout(() => {
_simulateRandomGesture(() => {
if (selection && seededRandom() < dragSelectionChance) {
exports.simulateRandomGesture(waitThenGo, {
shift: shiftStart,
shiftToggleChance,
secondary: secondaryStart,
secondaryToggleChance,
target: selection.canvas
});
} else {
waitThenGo();
}
});
}, delayBetweenGestures);
};
_simulateRandomGesture(waitThenGo);
};
exports.stopSimulatingGestures = () => {
if (exports.simulatingGestures) {
clearTimeout(gestureTimeoutID);
clearTimeout(periodicGesturesTimeoutID);
exports.simulatingGestures = false;
$status_text.default();
$cursor.remove();
cancel();
}
document.body.style.width = "";
document.body.style.height = "";
};
})(window);