xxxxxxxxxx
311
'use strict';
// Adjust these to alter how it behaves.
let period = 5;
let inc = 0.2;
let startDiam = 1;
// These record state. Don't need to change.
let step = 0;
let targetCenter;
let driftSamp = 0;
let driftInc = 0.00005;
let hOffset = 0;
// These affect the UI. You probably don't want to change.
let minInc = 0.05;
let maxInc = 10;
let minSld = 0;
let maxSld = 1000000;
let midSld = (minSld + maxSld) / 2;
let spnStepVal;
let sldPeriod;
let spnPeriodVal;
let sldInc;
let spnIncVal;
let sldH;
let spnHVal;
let sldPH;
let spnPHVal;
let sldV;
let spnVVal;
let sldPV;
let spnPVVal;
let doInc = true;
let chkClear;
function setup() {
createCanvas(windowWidth, windowHeight);
colorMode(HSB, 360, 100, 100, 100);
//cursor(CROSS);
noCursor();
initUI();
updateSpanVals();
driftTarget();
setCenter(targetCenter);
background(getFrameFg());
noFill();
stroke(0);
}
function draw() {
if (chkClear.checked()) {
background(getFrameBg());
}
inc = sldToInc(sldInc.value());
period = sldPeriod.value();
updateSpanVals();
push();
// Move the center closer to the target (if not there already)
let c = getCenter();
driftTarget();
if (!targetCenter.equals(getCenter())) {
let move = createVector(targetCenter.x - c.x, targetCenter.y - c.y);
move.setMag(move.mag() * 0.05);
setCenter(c.add(move));
}
// Find the max diam we will need
let maxD = findMaxD();
let requiredI = 0;
while (startDiam + pow(step + requiredI * period * inc, 2) < maxD) {
requiredI += 1;
}
// Just do as many as we need of them.
for (let i = requiredI; i >= 0; i--) {
// strokeWeight(4);
// stroke(36 * i, 50, 100);
strokeWeight(2);
stroke(getFrameFg());
let p = startDiam + pow(step + i * period * inc, 2);
drawStep(p);
}
pop();
//drawCenter();
push();
let oX = map(hOffset, 0, 360, 0, width);
fill(240, 10, 100, 1);
noStroke();
ellipse(oX, height / 2, 50, 50);
pop();
if (doInc) {
doStep();
}
}
// Increments the step counter according to increment and period.
function doStep() {
step = (step + inc) % (period * inc);
}
// Draws a center line.
function drawCenter() {
let c = getCenter();
stroke(100);
line(0, c.y, width, c.y);
line(c.x, 0, c.x, height);
}
// Draws a single step.
function drawStep(p) {
let [h, pH, v, pV] = gethpHvpV();
ellipse(h * width + pH * p, v * height + pV * p, p);
}
// Gets a color for the current frame
function getFrameHue(){
let base = map(pow(sin(frameCount / 1000), 4), -1, 1, -60, 60);
hOffset = (frameCount / 1000) % 360;
let h = (base + hOffset + 360) % 360;
return h;
}
// Gets a color for the current frame
function getFrameFg(){
return color(getFrameHue(), 80, 80);
}
// Gets a color for the current frame
function getFrameBg(){
return color((getFrameHue() + 120) % 360, 50, 50);
}
// Get the h and v props
function gethpHvpV() {
let h = sldToNorm(sldH.value());
let pH = sldToNorm(sldPH.value(), -1, 1);
let v = sldToNorm(sldV.value());
let pV = sldToNorm(sldPV.value(), -1, 1);
return [h, pH, v, pV];
}
// Finds the center of the recursion
function getCenter() {
let [h, pH, v, pV] = gethpHvpV();
let cX = h * width;
let cY = v * height;
return createVector(cX, cY);
}
// Finds the maximum drawable diameter given the
// current h and v props.
function findMaxD() {
let c = getCenter();
let lt = dist(c.x, c.y, 0, 0);
let rt = dist(c.x, c.y, width, 0);
let lb = dist(c.x, c.y, 0, height);
let rb = dist(c.x, c.y, width, height);
return 2 * max(lt, rt, lb, rb);
}
// Converts from a slider value to an increment.
// Maps linear slider onto an exponential scale.
function sldToInc(sld) {
let norm = sldToNorm(sld);
let exp = pow(norm, 4);
let inc = map(exp, 0, 1, minInc, maxInc);
return inc;
}
// Converts from an (exponential) increment scale to a slider value.
function incToSld(inc) {
let exp = map(inc, minInc, maxInc, 0, 1);
let norm = sqrt(sqrt(exp));
let sld = normToSld(norm);
return sld;
}
// Normalise a slider from minSld..maxSld to 0..1
function sldToNorm(sld, a = 0, b = 1) {
return map(sld, minSld, maxSld, a, b);
}
// Get a slider scaled number from a normalised range
function normToSld(norm, a = 0, b = 1) {
return map(norm, a, b, minSld, maxSld);
}
// Round a floating point number to a given precision
function roundTo(num, places) {
return round(num * places) / places;
}
// Updates the user interface values.
function updateSpanVals() {
spnPeriodVal.html(sldPeriod.value());
spnIncVal.html(sldToInc(sldInc.value()));
spnHVal.html(sldToNorm(sldH.value()));
spnPHVal.html(sldToNorm(sldPH.value(), -1, 1));
spnVVal.html(sldToNorm(sldV.value()));
spnPVVal.html(sldToNorm(sldPV.value(), -1, 1));
}
// Initialises the user interface (sliders etc.)
function initUI() {
createElement("br");
createSpan("step: ");
spnStepVal = createSpan(step);
createElement("br");
sldPeriod = createSlider(2, 200, period);
createSpan("period: ");
spnPeriodVal = createSpan();
createElement("br");
sldInc = createSlider(minSld, maxSld, incToSld(inc));
createSpan("inc: ");
spnIncVal = createSpan();
createElement("br");
sldH = createSlider(minSld, maxSld, midSld);
createSpan("h: ");
spnHVal = createSpan();
createElement("br");
sldPH = createSlider(minSld, maxSld, midSld);
createSpan("pH: ");
spnPHVal = createSpan();
createElement("br");
sldV = createSlider(minSld, maxSld, midSld);
createSpan("v: ");
spnVVal = createSpan();
createElement("br");
sldPV = createSlider(minSld, maxSld, midSld);
createSpan("pV: ");
spnPVVal = createSpan();
chkClear = createCheckbox("clear", false);
}
function driftTarget(){
driftSamp += driftInc;
let dx = noise(driftSamp, 0);
let dy = noise(driftSamp, 1000);
dx = map(dx, 0, 1, 0, width);
dy = map(dy, 0, 1, 0, height);
targetCenter = createVector(dx, dy);
}
function setCenter(vec) {
// Figure out what the new h and v props should be
let h = map(vec.x, 0, width, 0, 1);
h = normToSld(h);
let v = map(vec.y, 0, height, 0, 1);
v = normToSld(v);
// Figure out an inverse value for the pH and pV props
let pAmt = 0.3;
let pH = map(vec.x, 0, width, -pAmt, pAmt);
pH = normToSld(-pH, -1, 1);
let pV = map(vec.y, 0, height, -pAmt, pAmt);
pV = normToSld(-pV, -1, 1);
sldH.value(h);
sldV.value(v);
sldPH.value(pH);
sldPV.value(pV);
}
// Returns true if the given point is over the canvas, false otherwise.
function isOverCanvas(x, y){
return x > 0 && x < width && y > 0 && y < height;
}
function keyPressed() {
if (key == ' ' && !doInc) {
doStep();
} else {
doInc = !doInc
}
}
function mouseClicked() {
if (isOverCanvas(mouseX, mouseY)){
targetCenter = createVector(mouseX, mouseY);
}
}
function mouseDragged() {
if (isOverCanvas(mouseX, mouseY)){
targetCenter = createVector(mouseX, mouseY);
}
}