Better mouse movement; add custom input box

This commit is contained in:
Logan Hunt 2022-06-08 01:57:28 -07:00
parent 9caedf1aac
commit 638acfc48f
Signed by untrusted user who does not match committer: simponic
GPG Key ID: 52B3774857EB24B1
2 changed files with 49 additions and 61 deletions

View File

@ -36,11 +36,11 @@
<input orient="vertical" type="range" min="-1000" max="1000" value="0" id="imaginarySlider" class="verticalSlider"> <input orient="vertical" type="range" min="-1000" max="1000" value="0" id="imaginarySlider" class="verticalSlider">
<input type="range" min="-1000" max="1000" value="0" id="realSlider"> <input type="range" min="-1000" max="1000" value="0" id="realSlider">
<br> <br>
imaginary: <span id="imag-val"></span> imaginary: <input id="imag-val" size="10" type="number" step="0.001"></input>
<button id="animate-imag" onclick="startImagAnim()">Animate</button> <button id="animate-imag" onclick="startAnim('animate-imag', 'ci')">Animate</button>
<br> <br>
real: <span id="real-val"></span> real: <input id="real-val" size="10" type="number" step="0.001"></input>
<button id="animate-real" onclick="startRealAnim()">Animate</button> <button id="animate-real" onclick="startAnim('animate-real', 'cr')">Animate</button>
<br> <br>
powered by <a href="https://github.com/gpujs/gpu.js">gpu.js</a> powered by <a href="https://github.com/gpujs/gpu.js">gpu.js</a>
</div> </div>

View File

@ -1,6 +1,7 @@
const MIN_ZOOM = 0.0001; const MIN_ZOOM = 0.0001;
const MAX_ZOOM = 4; const MAX_ZOOM = 4;
const SLIDER_DIV = 1000; const C_THRESHOLD = Math.sqrt(2)/2;
const SLIDER_DIV = 1000*C_THRESHOLD;
const gpu = new GPU(); const gpu = new GPU();
const buildRender = (width, height) => gpu.createKernel(function (maxIterations, cr, ci, centerX, centerY, zoom, colorMultipliers) { const buildRender = (width, height) => gpu.createKernel(function (maxIterations, cr, ci, centerX, centerY, zoom, colorMultipliers) {
@ -11,9 +12,6 @@ const buildRender = (width, height) => gpu.createKernel(function (maxIterations,
const xtemp = zx * zx - zy * zy + cr; const xtemp = zx * zx - zy * zy + cr;
zy = 2 * zx * zy + ci; zy = 2 * zx * zy + ci;
zx = xtemp; zx = xtemp;
// filled Julia set for Q_c exists only when z has radius less than 2 and less than |c|
// and we are only allowing |c| < 2
if (zx * zx + zy * zy > 4) { if (zx * zx + zy * zy > 4) {
iterations = i; iterations = i;
break; break;
@ -29,13 +27,13 @@ const buildRender = (width, height) => gpu.createKernel(function (maxIterations,
const canvasHolder = document.getElementById('canvasHolder'); const canvasHolder = document.getElementById('canvasHolder');
let render; // The GPU kernel built from buildRender let render; // The GPU kernel built from buildRender
let state = { let state = {
colorMultipliers: [0.01 * Math.random() + 0.005, 0.03 * Math.random() + 0.005, 0.02 * Math.random() + 0.005], colorMultipliers: [0.01 * Math.random() + 0.015, 0.03 * Math.random() + 0.007, 0.02 * Math.random() + 0.010],
changes: { changes: {
centerX: 0, centerX: 0,
centerY: 0, centerY: 0,
zoom: 3, zoom: 3,
cr: parseFloat(document.getElementById("realSlider").value) / SLIDER_DIV, cr: parseFloat(document.getElementById('realSlider').value) / SLIDER_DIV,
ci: parseFloat(document.getElementById("imaginarySlider").value) / SLIDER_DIV, ci: parseFloat(document.getElementById('imaginarySlider').value) / SLIDER_DIV,
maxIterations: 1000, maxIterations: 1000,
width: document.body.clientWidth, width: document.body.clientWidth,
height: document.body.clientHeight, height: document.body.clientHeight,
@ -43,6 +41,7 @@ let state = {
}; };
const doRender = (renderF, state) => { const doRender = (renderF, state) => {
// gpu.js doesn't support JS objects as kernel parameters - https://github.com/gpujs/gpu.js/issues/245
renderF(state.maxIterations, state.cr, state.ci, state.centerX, state.centerY, state.zoom, state.colorMultipliers); renderF(state.maxIterations, state.cr, state.ci, state.centerX, state.centerY, state.zoom, state.colorMultipliers);
}; };
@ -54,11 +53,13 @@ const loop = () => {
render = buildRender(state.width, state.height); render = buildRender(state.width, state.height);
canvasHolder.appendChild(render.canvas); canvasHolder.appendChild(render.canvas);
} }
if (state.changes.ci !== undefined) { if (typeof state.changes.ci !== 'undefined') {
document.getElementById("imag-val").innerHTML = state.ci.toFixed(4); document.getElementById('imag-val').value = state.ci.toFixed(4);
document.getElementById('imaginarySlider').value = state.changes.ci * SLIDER_DIV;
} }
if (state.changes.cr !== undefined) { if (typeof state.changes.cr !== 'undefined') {
document.getElementById("real-val").innerHTML = state.cr.toFixed(4); document.getElementById('real-val').value = state.cr.toFixed(4);
document.getElementById('realSlider').value = state.changes.cr * SLIDER_DIV;
} }
state.changes = {}; state.changes = {};
doRender(render, state); doRender(render, state);
@ -71,72 +72,59 @@ loop();
// NOTE: UI code is trash lol // NOTE: UI code is trash lol
const startImagAnim = () => { const startAnim = (sliderId, complexComponentName='ci') => {
const start = setInterval(() => { const restart = (interval) => {
if (state.ci >= 1) { clearInterval(interval);
clearInterval(start); document.getElementById(sliderId).innerHTML = 'Animate';
document.getElementById("animate-imag").innerHTML = "Animate"; document.getElementById(sliderId).onclick = ()=>startAnim(sliderId, complexComponentName);
document.getElementById("animate-imag").onclick = startImagAnim;
return; return;
};
const start = setInterval(() => {
if (state[complexComponentName] >= C_THRESHOLD) {
restart(start);
} }
state.changes.ci = state.ci + 0.001; state.changes[complexComponentName] = state[complexComponentName] + 0.001;
document.getElementById("imaginarySlider").value = state.changes.ci * 1000;
}, 1000/60); }, 1000/60);
document.getElementById("animate-imag").innerHTML = "Stop"; document.getElementById(sliderId).innerHTML = 'Stop';
document.getElementById("animate-imag").onclick = () => { document.getElementById(sliderId).onclick = ()=>restart(start);
clearInterval(start);
document.getElementById("animate-imag").innerHTML = "Animate";
document.getElementById("animate-imag").onclick = startImagAnim;
};
}; };
const startRealAnim = () => { document.getElementById('realSlider').oninput = function() {
const start = setInterval(() => {
if (state.cr >= 1) {
clearInterval(start);
document.getElementById("animate-real").innerHTML = "Animate";
document.getElementById("animate-real").onclick = startRealAnim;
return;
}
state.changes.cr = state.cr + 0.001;
document.getElementById("realSlider").value = state.changes.cr * 1000;
}, 1000/60);
document.getElementById("animate-real").innerHTML = "Stop";
document.getElementById("animate-real").onclick = () => {
clearInterval(start);
document.getElementById("animate-real").innerHTML = "Animate";
document.getElementById("animate-real").onclick = startRealAnim;
};
};
document.getElementById("realSlider").oninput = function() {
state.changes.cr = parseFloat(this.value) / SLIDER_DIV; state.changes.cr = parseFloat(this.value) / SLIDER_DIV;
}; };
document.getElementById('imaginarySlider').oninput = function() {
document.getElementById("imaginarySlider").oninput = function() {
state.changes.ci = parseFloat(this.value) / SLIDER_DIV; state.changes.ci = parseFloat(this.value) / SLIDER_DIV;
}; };
canvasHolder.onwheel = (event) => { document.getElementById('imag-val').addEventListener('change', function (e) {
state.changes.zoom = Math.min(Math.max(state.zoom + event.deltaY * 0.001 * state.zoom, MIN_ZOOM), MAX_ZOOM); state.changes.ci = parseFloat(this.value);
} });
document.getElementById('real-val').addEventListener('change', function (e) {
state.changes.cr = parseFloat(this.value);
});
canvasHolder.addEventListener('wheel', (e) => {
e.preventDefault();
state.changes.zoom = Math.min(Math.max(state.zoom + e.deltaY * 0.001 * state.zoom, MIN_ZOOM), MAX_ZOOM);
});
let isDown = false; let isDown = false;
canvasHolder.addEventListener('mousedown', function(e) { canvasHolder.addEventListener('mousedown', (e) => {
e.preventDefault();
isDown = true; isDown = true;
}, true); }, true);
canvasHolder.addEventListener('mouseup', function() { canvasHolder.addEventListener('mouseup', (e) => {
e.preventDefault();
isDown = false; isDown = false;
}, true); }, true);
canvasHolder.addEventListener('mousemove', function(event) { canvasHolder.addEventListener('mousemove', (e) => {
event.preventDefault(); e.preventDefault();
if (isDown) { if (isDown) {
let deltaX = -event.movementX * state.zoom / 700; let deltaX = -e.movementX * state.zoom / document.body.clientWidth;
let deltaY = event.movementY * state.zoom / 700; let deltaY = e.movementY * state.zoom / document.body.clientHeight;
state.changes.centerX = state.centerX + deltaX; state.changes.centerX = state.centerX + deltaX;
state.changes.centerY = state.centerY + deltaY; state.changes.centerY = state.centerY + deltaY;
} }