diff --git a/public/index.html b/public/index.html index 8d6652f..5f3bdb9 100644 --- a/public/index.html +++ b/public/index.html @@ -2,16 +2,21 @@ + + + + Gray Scott Plus! -

Gray Scott Plus!

+
+ \ No newline at end of file diff --git a/public/js/cell.js b/public/js/cell.js new file mode 100644 index 0000000..c03805c --- /dev/null +++ b/public/js/cell.js @@ -0,0 +1,87 @@ +//cell class +class Cell { + constructor(x, y, a, b, noiseX, noiseY, noiseScalar) + { + this.x = x; + this.y = y; + + + this.setAB = (a, b) => + { + this.a = constrain(a, 0, 1); + this.b = constrain(b, 0, 1); + } + this.setAB(a, b); + + this.zeroWeights = () => + { + this.weightLftUp = weightDiagonal; + this.weightCtrUp = weightOrtho; + this.weightRgtUp = weightDiagonal; + this.weightLftCr = weightOrtho; + this.weightCtrCr = weightReset; + this.weightRgtCr = weightOrtho; + this.weightLftDn = weightDiagonal; + this.weightCtrDn = weightOrtho; + this.weightRgtDn = weightDiagonal; + } + this.zeroWeights(); + + //calculate individual bias weight + this.calcBiasIndiv = (dir, biasX, biasY, baseWeight, scalar) => + { + const sumX = dir.x + biasX; + const sumY = dir.y + biasY; + + return sqrt((sumX * sumX) + (sumY * sumY)) * baseWeight * scalar; + } + + //calculate new bias weights + this.calcWeights = (biasX, biasY, scalar) => + { + let total = 0; + total += this.weightLftUp = weightDiagonal + this.calcBiasIndiv(dirLftUp, biasX, biasY, weightDiagonal, scalar); + total += this.weightCtrUp = weightOrtho + this.calcBiasIndiv(dirCtrUp, biasX, biasY, weightOrtho, scalar); + total += this.weightRgtUp = weightDiagonal + this.calcBiasIndiv(dirRgtUp, biasX, biasY, weightDiagonal, scalar); + total += this.weightLftCr = weightOrtho + this.calcBiasIndiv(dirLftCr, biasX, biasY, weightOrtho, scalar); + //total += weightCtrCr = weightReset; + total += this.weightRgtCr = weightOrtho + this.calcBiasIndiv(dirRgtCr, biasX, biasY, weightOrtho, scalar); + total += this.weightLftDn = weightDiagonal + this.calcBiasIndiv(dirLftDn, biasX, biasY, weightDiagonal, scalar); + total += this.weightCtrDn = weightOrtho + this.calcBiasIndiv(dirCtrDn, biasX, biasY, weightOrtho, scalar); + total += this.weightRgtDn = weightDiagonal + this.calcBiasIndiv(dirRgtDn, biasX, biasY, weightDiagonal, scalar); + + this.weightLftUp /= total; + this.weightCtrUp /= total; + this.weightRgtUp /= total; + this.weightLftCr /= total; + + this.weightRgtCr /= total; + this.weightLftDn /= total; + this.weightCtrDn /= total; + this.weightRgtDn /= total; + + //console.log(weightLftUp); + } + + this.setWeights = (noiseX, noiseY, scalar) => + { + this.noiseX = noiseX; + this.noiseY = noiseY; + + //set bias as unitized noise values + const mag = sqrt((noiseX * noiseX) + (noiseY * noiseY)); + const biasX = noiseX / mag; + const biasY = noiseY / mag; + + this.calcWeights(biasX, biasY, scalar); + } + this.setWeights(noiseX, noiseY, noiseScalar); + + this.setWeightScalar = (scalar) => + { + this.setWeights(this.noiseX, this.noiseY, scalar); + } + + return this; + } +} \ No newline at end of file diff --git a/public/js/global.js b/public/js/global.js new file mode 100644 index 0000000..e06eb90 --- /dev/null +++ b/public/js/global.js @@ -0,0 +1,57 @@ +const uiWidth = 256; //width of the ui bar +const w = 512; //totalWidth - uiWidth; //grid width. TODO: make this resolution adjustable and interpolate while drawing +const totalWidth = w + uiWidth;//innerWidth; //total width of the p5 canvas. TODO: make this responsive to resizing +const h = 512; //grid height. TODO: make this resolution scalable and interpolate while drawing +const vecStep = 8; + +//let gridNoise = []; //grid current state noise +let gridNow = []; //grid current state +let gridNext = []; //grid future state +let gridVec = []; //grid vector state + +const weightReset = -1; //reset weight for cell self +const weightOrtho = 0.2; //base weight for cell ortho neighbors +const weightDiagonal = 0.05; //base weight for cell diagonal neighbors +const deltaA = 1.0; //base speed for A +const deltaB = 0.5; //base speed for B +let feedRate = 0.029; //0.025; //0.0w55; +let killRate = 0.057; //0.06; //0.062; +let timeScalar = 1.0; //speed dampener +let paintRangeScalar = 0.1; //paint brush radius +let noiseScalar = 0.005; +let vecScalar = 0.03; + +feedKillPresets = { + "mazes": {feed: 0.029, kill: 0.057}, + "solitions": {feed: 0.03, kill: 0.062}, + "pulsating solitions": {feed: 0.025, kill: 0.062}, + "worms": {feed: 0.078, kill: 0.061}, + "holes": {feed: 0.039, kill: 0.058}, + "chaos": {feed: 0.026, kill: 0.051}, + "chaos and holes (by clem)": {feed: 0.034, kill: 0.056}, + "moving spots": {feed: 0.014, kill: 0.054}, + "spots and loops": {feed: 0.018, kill: 0.051}, + "waves": {feed: 0.014, kill: 0.045}, + "the u-skate world": {feed: 0.062, kill: 0.06093} +}; + +let dirLftUp = null; //left up vector +let dirCtrUp = null; //center up vector +let dirRgtUp = null; //right up vector +let dirLftCr = null; //left center vector +let dirCtrCr = null; //center vector (0, 0) +let dirRgtCr = null; //right center vector +let dirLftDn = null; //left down vector +let dirCtrDn = null; //center down vector +let dirRgtDn = null; //right down vector + +//let biasVec = null; //bias vector +let weightLftUp = weightDiagonal; //left up result weight +let weightCtrUp = weightOrtho; //center up result weight +let weightRgtUp = weightDiagonal; //right up result weight +let weightLftCr = weightOrtho; //left center result weight +let weightCtrCr = weightReset; //center reset weight (-1) +let weightRgtCr = weightOrtho; //right center result weight +let weightLftDn = weightDiagonal; //left down result weight +let weightCtrDn = weightOrtho; //center down result weight +let weightRgtDn = weightDiagonal; //right down result weight \ No newline at end of file diff --git a/public/js/main.js b/public/js/main.js index efe9fbf..4a00859 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,51 +1,82 @@ -const uiWidth = 256; //width of the ui bar -const totalWidth = innerWidth; //total width of the p5 canvas. TODO: make this responsive to resizing -const w = totalWidth - uiWidth; //grid width. TODO: make this resolution adjustable and interpolate while drawing -const h = 512; //grid height. TODO: make this resolution scalable and interpolate while drawing - -let gridNow = []; //grid current state -let gridNext = []; //grid future state - -const weightReset = -1; //reset weight for cell self -const weightOrtho = 0.2; //base weight for cell ortho neighbors -const weightDiagonal = 0.05; //base weight for cell diagonal neighbors -const deltaA = 1.0; //base speed for A -const deltaB = 0.5; //base speed for B -let feedRate = 0.025; //0.0w55; -let killRate = 0.06; //0.062; -let timeScalar = 1.0; //speed dampener -let paintRangeScalar = 0.1; //paint brush radius - -let dirLftUp = null; //left up vector -let dirCtrUp = null; //center up vector -let dirRgtUp = null; //right up vector -let dirLftCr = null; //left center vector -let dirCtrCr = null; //center vector (0, 0) -let dirRgtCr = null; //right center vector -let dirLftDn = null; //left down vector -let dirCtrDn = null; //center down vector -let dirRgtDn = null; //right down vector - -let biasVec = null; //bias vector -let weightLftUp = weightDiagonal; //left up result weight -let weightCtrUp = weightOrtho; //center up result weight -let weightRgtUp = weightDiagonal; //right up result weight -let weightLftCr = weightOrtho; //left center result weight -let weightCtrCr = weightReset; //center reset weight (-1) -let weightRgtCr = weightOrtho; //right center result weight -let weightLftDn = weightDiagonal; //left down result weight -let weightCtrDn = weightOrtho; //center down result weight -let weightRgtDn = weightDiagonal; //right down result weight +//const { text } = require("express"); + +const sliderWidth = uiWidth * 0.8; +const sliderOffsetX = uiWidth * 0.1; +const titleOffsetY = h * 0.17; +const sliderStepY = h * 0.1; +const sliderStep = 0.001; + +let canvas = null; +let sliderFeedRate = null; +let sliderKillRate = null; +let sliderTimeScalar = null; +let sliderPaintRangeScalar = null; +let sliderNoiseScalar = null; +let sliderVecScalar = null; +let dropDownFeedKill = null; +let buttonReset = null; //p5 setup. This is called once at the beginning of runtime setup = () => { //console.log("setup!"); + //setAttributes('willReadFrequently', true); + + const e = createCanvas(totalWidth, h); + canvas = e.canvas; + + drawingContext.willReadFrequently = true; + setAttributes('2d', 'willReadFrequently', true); + + // e.canvas.willReadFrequently = true; + // e.drawingContext.willReadFrequently = true; + // //e.drawingContext.getContext('2d', { willReadFrequently: true }); + // e.canvas.getContext('2d', { willReadFrequently: true }); + //console.log(drawingContext); + // setAttributes('willReadFrequently', true); - createCanvas(innerWidth, h); pixelDensity(1); + + //bias = createVector(biasStep, biasStep); + initDir(); + initGrids(w, h); - bias = createVector(biasStep, biasStep); + initUI(); + + setGridCircle(0.5); + + // const halfW = round(w * 0.5); + // const halfH = round(h * 0.5); + // const offset = round(w * 0.1); + + // //temp - make this interactive! + // for(let x = halfW - offset; x < halfW + offset; x++) + // { + // for(let y = halfH - offset; y < halfH + offset; y++) + // { + // gridNow[x][y].b = 1; + // } + // } +} + +//p5 draw loop. This is called every frame +draw = () => { + + background(255); + //background(0,0,0,0); + + paintCheck(); + + updateGrid(); + drawPixels(); + step(); + + drawUILabels(); + drawVecGrid(); + drawPaintRadius(); +} + +initDir = () => { dirLftUp = createVector(-1, -1); dirCtrUp = createVector(0, -1); dirRgtUp = createVector(1, -1); @@ -55,51 +86,240 @@ setup = () => { dirLftDn = createVector(-1, 1); dirCtrDn = createVector(0, 1); dirRgtDn = createVector(1, 1); +} - resetBias(); - updateBias(bias); +//initialize grids +initGrids = (w, h) => { + + initCellGrids(w, h); + initVecGrid(w, h); +} - const halfW = round(w * 0.5); - const halfH = round(h * 0.5); - const offset = round(w * 0.1); +initCellGrids = (w, h) => { - //temp - make this interactive! - for(let x = halfW - offset; x < halfW + offset; x++) + //const gridWidth = w - uiWidth; + for(let x = 0; x < w; x++) { - for(let y = halfH - offset; y < halfH + offset; y++) + //gridNoise[x] = []; + gridNow[x] = []; + gridNext[x] = []; + + for(let y = 0; y < h; y++) { - gridNow[x][y].b = 1; + //const noiseY = noise(map(y, 0, h, 0, noiseScalar)); + + const mNX = gen2dUnitNoise((w + x) * noiseScalar, y * noiseScalar); + const mNY = gen2dUnitNoise(x * noiseScalar, (h + y) * noiseScalar); + + gridNow[x][y] = new Cell(x, y, 1.0, 0.0, mNX, mNY, vecScalar); + gridNext[x][y] = new Cell(x, y, 1.0, 0.0, mNX, mNY, vecScalar); } } } -//p5 draw loop. This is called every frame -draw = () => { +initVecGrid = (w, h) => { +// const vecGridDimX = w / vecStep; + // const vecGridDimY = h / vecStep; + for(let x = 0; x < w; x += vecStep) + { + const vecX = x / vecStep; + //const avgX = getAvg(noiseX, vecX, vecX + vecStep); + gridVec[vecX] = []; + + for(let y = 0; y < h; y += vecStep) + { + const vecY = y / vecStep; + gridVec[vecX][vecY] = getCellAvg(x, x + vecStep, y, y + vecStep); + } + } +} - background(200); +initUI = () => { + + sliderNoiseScalar = createSlider(0.001, 0.01, noiseScalar, sliderStep); + sliderVecScalar = createSlider(0, 0.05, vecScalar, sliderStep); + sliderFeedRate = createSlider(0.01, 0.08, feedRate, sliderStep); + sliderKillRate = createSlider(0.01, 0.08, killRate, sliderStep); + sliderTimeScalar = createSlider(0.1, 1.1, timeScalar, sliderStep); + sliderPaintRangeScalar = createSlider(0.01, 0.25, paintRangeScalar, sliderStep); + dropDownFeedKill = createSelect(); + dropDownFeedKill.option("mazes"); + dropDownFeedKill.option("solitions"); + dropDownFeedKill.option("pulsating solitions"); + dropDownFeedKill.option("worms"); + dropDownFeedKill.option("holes"); + dropDownFeedKill.option("chaos"); + dropDownFeedKill.option("chaos and holes (by clem)"); + dropDownFeedKill.option("moving spots"); + dropDownFeedKill.option("spots and loops"); + dropDownFeedKill.option("waves"); + dropDownFeedKill.option("the u-skate world"); + buttonReset = createButton('reset'); + + + // sliderNoiseScalar.parent(document.getElementsByTagName('main')[0]); + // sliderNoiseScalar.addClass('mySlider'); + // console.log(sliderNoiseScalar); + + sliderNoiseScalar.mouseReleased(() => { + noiseScalar = sliderNoiseScalar.value(); + updateCellNoise(w, h); + }); + sliderVecScalar.mouseReleased(() => { + vecScalar = sliderVecScalar.value(); + updateCellNoiseVecScalar(vecScalar); + }); + sliderFeedRate.mouseReleased(() => {feedRate = sliderFeedRate.value();}); + sliderKillRate.mouseReleased(() => {killRate = sliderKillRate.value();}); + sliderTimeScalar.mouseReleased(() => {timeScalar = sliderTimeScalar.value();}); + sliderPaintRangeScalar.mouseReleased(() => {paintRangeScalar = sliderPaintRangeScalar.value();}); + dropDownFeedKill.changed(() => { + const presetName = dropDownFeedKill.value(); + const preset = feedKillPresets[presetName]; + feedRate = preset.feed; + killRate = preset.kill; + sliderFeedRate.value(feedRate); + sliderKillRate.value(killRate); + + //TODO: clear and reset grid + }); + buttonReset.mouseReleased(() => {resetGrid();}); + + // sliderNoiseScalar.parent(canvas); + // sliderNoiseScalar.position(sliderOffsetX, sliderStepY, 'relative'); + + sliderNoiseScalar.position(sliderOffsetX, titleOffsetY + sliderStepY); + sliderVecScalar.position(sliderOffsetX, titleOffsetY + sliderStepY * 2); + sliderFeedRate.position(sliderOffsetX, titleOffsetY + sliderStepY * 3); + sliderKillRate.position(sliderOffsetX, titleOffsetY + sliderStepY * 4); + sliderTimeScalar.position(sliderOffsetX, titleOffsetY + sliderStepY * 5); + sliderPaintRangeScalar.position(sliderOffsetX, titleOffsetY + sliderStepY * 6); + dropDownFeedKill.position(sliderOffsetX, titleOffsetY + sliderStepY * 7.15); + buttonReset.position(sliderOffsetX, titleOffsetY + sliderStepY * 8); + + sliderNoiseScalar.size(sliderWidth); + sliderVecScalar.size(sliderWidth); + sliderFeedRate.size(sliderWidth); + sliderKillRate.size(sliderWidth); + sliderTimeScalar.size(sliderWidth); + sliderPaintRangeScalar.size(sliderWidth); + dropDownFeedKill.size(sliderWidth); + buttonReset.size(sliderWidth); +} - paintCheck(); +getCellAvg = (startX, endX, startY, endY) => +{ + let avgX = 0; + let avgY = 0 + const divX = endX - startX; + const divY = endY - startY; - updateGrid(); - drawPixels(); - step(); + for(let x = startX; x < endX && x < gridNow.length; x++) + { + for(let y = startY; y < endY && y < gridNow[x].length; y++) + { + const cell = gridNow[x][y]; + avgX += cell.noiseX; + avgY += cell.noiseY; + } + } + + avgX /= divX; + avgY /= divY; + + const vec = createVector(avgX , avgY); + vec.normalize(); + + return vec; } -//initialize grids -initGrids = (w, h) => { +getAvg = (arr, start, end) => +{ + let avg = 0; + const divisor = end - start; + + for(let i = start; i < end && i < arr.length; i++) + { + avg += arr[i]; + } - //const gridWidth = w - uiWidth; + return avg / divisor; +} + +gen2dUnitNoise = (x, y) => { + return map(noise(x, y), 0, 1, -1, 1); +} + +updateCellNoise = (w, h) => { + for(let x = 0; x < w; x++) { - gridNow[x] = []; - gridNext[x] = []; + for(let y = 0; y < h; y++) + { + //const noiseY = noise(map(y, 0, h, 0, noiseScalar)); + + const mNX = gen2dUnitNoise((w + x) * noiseScalar, y * noiseScalar); + const mNY = gen2dUnitNoise(x * noiseScalar, (h + y) * noiseScalar); + + gridNow[x][y].setWeights(mNX, mNY, vecScalar); + gridNext[x][y].setWeights(mNX, mNY, vecScalar); + } + } + + initVecGrid(w, h); +} + +updateCellNoiseVecScalar = (w, h) => { + + for(let x = 0; x < w; x++) + { + for(let y = 0; y < h; y++) + { + gridNow[x][y].setWeightScalar(vecScalar); + gridNext[x][y].setWeightScalar(vecScalar); + } + } +} + +resetGrid = () => { + clearGrid(); + setGridCircle(0.5); +} +clearGrid = () => { + for(let x = 0; x < w; x++) + { for(let y = 0; y < h; y++) { - gridNow[x][y] = new Cell(x, y, 1.0, 0.0); - gridNext[x][y] = new Cell(x, y, 1.0, 0.0); + gridNow[x][y].setAB(1, 0); + gridNext[x][y].setAB(1, 0); + } + } +} + +setGridCircle = (sizeRatio) => { + + const xStart = round((w * 0.5) - (w * sizeRatio * 0.5)); + const xEnd = w - xStart; + const xCenter = round(w * 0.5); + const yCenter = round(h * 0.5); + const pi = Math.PI; + + for(let x = xStart; x < xEnd; x++) + { + const xUnit = (x - xStart) / (xEnd - xStart); + const xOffset = round(Math.cos(xUnit * pi) * w * sizeRatio * 0.5); + const yOffset = round(Math.sin(xUnit * pi) * h * sizeRatio * 0.5); + const xPos = xCenter + xOffset; + + for(let y = yCenter - yOffset; y < yCenter + yOffset; y++) + { + const yPos = y; + + gridNow[xPos][yPos].b = 1; } } + } //update future grid @@ -111,132 +331,77 @@ updateGrid = () => { { const cellNow = gridNow[x][y]; const cellNext = gridNext[x][y]; - const aNext = grayScott(cellNow, deltaA, laplaceA, getA, reactionA, feed); - const bNext = grayScott(cellNow, deltaB, laplaceB, getB, reactionB, kill); + const aNext = grayScott(cellNow, cellNow.a, deltaA, laplaceA, reactionA, feed); + const bNext = grayScott(cellNow, cellNow.b, deltaB, laplaceB, reactionB, kill); - cellNext.set(aNext, bNext); + cellNext.setAB(aNext, bNext); } } } -//ye olde gray scot model -grayScott = (cell, delta, laplace, getAB, reaction, feedKill) => { - - const v = getAB(cell); +//ye olde gray scott model +grayScott = (cell, value, delta, laplace, reaction, feedKill) => { - const stepOne = (delta * laplace(cell.x, cell.y, getAB)) + + const stepOne = (delta * laplace(cell)) + reaction(cell.a, cell.b) + - feedKill(v); + feedKill(value); const stepScale = stepOne * timeScalar; - return v + stepScale; + return value + stepScale; } //laplace function for feed values -laplaceA = (x, y, f) => { +laplaceA = (cell) => { - left = (x + w - 1) % w; - right = (x + 1) % w; - up = (y + h - 1) % h; - down = (y + 1) % h; + const x = cell.x; + const y = cell.y; + const left = (x + w - 1) % w; + const right = (x + 1) % w; + const up = (y + h - 1) % h; + const down = (y + 1) % h; let v = 0; - v += f(gridNow[x][y]) * weightReset; - v += f(gridNow[left][y]) * weightLftCr; - v += f(gridNow[right][y]) * weightRgtCr; - v += f(gridNow[x][down]) * weightCtrDn; - v += f(gridNow[x][up]) * weightCtrUp; - v += f(gridNow[left][up]) * weightLftUp; - v += f(gridNow[right][up]) * weightRgtUp; - v += f(gridNow[right][down]) * weightRgtDn; - v += f(gridNow[left][down]) * weightLftDn; + v += gridNow[x][y].a * weightReset; + v += gridNow[left][y].a * cell.weightLftCr; + v += gridNow[right][y].a * cell.weightRgtCr; + v += gridNow[x][down].a * cell.weightCtrDn; + v += gridNow[x][up].a * cell.weightCtrUp; + v += gridNow[left][up].a * cell.weightLftUp; + v += gridNow[right][up].a * cell.weightRgtUp; + v += gridNow[right][down].a * cell.weightRgtDn; + v += gridNow[left][down].a * cell.weightLftDn; return v; } //laplace function for kill values //laplaceB weights should be mirrored to laplaceA -laplaceB = (x, y, f) => { +laplaceB = (cell) => { - left = (x + w - 1) % w; - right = (x + 1) % w; - up = (y + h - 1) % h; - down = (y + 1) % h; + const x = cell.x; + const y = cell.y; + const left = (x + w - 1) % w; + const right = (x + 1) % w; + const up = (y + h - 1) % h; + const down = (y + 1) % h; let v = 0; - v += f(gridNow[x][y]) * weightReset; - v += f(gridNow[left][y]) * weightRgtCr; - v += f(gridNow[right][y]) * weightLftCr; - v += f(gridNow[x][down]) * weightCtrUp; - v += f(gridNow[x][up]) * weightCtrDn; - v += f(gridNow[left][up]) * weightRgtDn; - v += f(gridNow[right][up]) * weightLftDn; - v += f(gridNow[right][down]) * weightLftUp; - v += f(gridNow[left][down]) * weightRgtUp; + v += gridNow[x][y].b * weightReset; + v += gridNow[left][y].b * cell.weightRgtCr; + v += gridNow[right][y].b * cell.weightLftCr; + v += gridNow[x][down].b * cell.weightCtrUp; + v += gridNow[x][up].b * cell.weightCtrDn; + v += gridNow[left][up].b * cell.weightRgtDn; + v += gridNow[right][up].b * cell.weightLftDn; + v += gridNow[right][down].b * cell.weightLftUp; + v += gridNow[left][down].b * cell.weightRgtUp; return v; } -//update bias vector and calculate new bias weights -updateBias = (delta) => { - - bias = p5.Vector.add(bias, delta); - - calcBias(bias); -} - -//reset bias weights -resetBias = () => { - weightLftUp = weightDiagonal; - weightCtrUp = weightOrtho; - weightRgtUp = weightDiagonal; - weightLftCr = weightOrtho; - weightCtrCr = weightReset; - weightRgtCr = weightOrtho; - weightLftDn = weightDiagonal; - weightCtrDn = weightOrtho; - weightRgtDn = weightDiagonal; -} - -//calculate new bias weights -calcBias = (bias) => { - - resetBias(); - - let total = 0; - total += weightLftUp = weightDiagonal + calcBiasIndiv(dirLftUp, bias, weightDiagonal); - total += weightCtrUp = weightOrtho + calcBiasIndiv(dirCtrUp, bias, weightOrtho); - total += weightRgtUp = weightDiagonal + calcBiasIndiv(dirRgtUp, bias, weightDiagonal); - total += weightLftCr = weightOrtho + calcBiasIndiv(dirLftCr, bias, weightOrtho); - //total += weightCtrCr = weightReset; - total += weightRgtCr = weightOrtho + calcBiasIndiv(dirRgtCr, bias, weightOrtho); - total += weightLftDn = weightDiagonal + calcBiasIndiv(dirLftDn, bias, weightDiagonal); - total += weightCtrDn = weightOrtho + calcBiasIndiv(dirCtrDn, bias, weightOrtho); - total += weightRgtDn = weightDiagonal + calcBiasIndiv(dirRgtDn, bias, weightDiagonal); - - weightLftUp /= total; - weightCtrUp /= total; - weightRgtUp /= total; - weightLftCr /= total; - - weightRgtCr /= total; - weightLftDn /= total; - weightCtrDn /= total; - weightRgtDn /= total; -} - -//calculate individual bias weight -calcBiasIndiv = (dir, bias, baseWeight) => -{ - const sumX = dir.x + bias.x; - const sumY = dir.y + bias.y; - - return sqrt((sumX * sumX) + (sumY * sumY)) * baseWeight * 0.25; -} - -getA = (c) => { return c.a; } //return cell a value -getB = (c) => { return c.b; } //return cell b value +// getA = (c) => { return c.a; } //return cell a value +// getB = (c) => { return c.b; } //return cell b value reactionA = (a, b) => { return a * b * b * -1;} //reaction function for cell a value reactionB = (a, b) => { return a * b * b;} //reaction function for cell b value feed = (a) => { return feedRate * (1.0 - a); } //feed function @@ -253,7 +418,7 @@ drawPixels = () => { const index = (uiWidth + x + y * totalWidth) * 4; //const index = (uiWidth + x + y * w) * 4; const cell = gridNext[x][y]; - const value = constrain(floor((cell.a - cell.b) * 255.0), 0, 200); + const value = constrain(floor((cell.a - cell.b) * 255.0), 0, 255); pixels[index + 0] = value; //r pixels[index + 1] = value; //g @@ -265,6 +430,66 @@ drawPixels = () => { updatePixels(); } +drawUILabels = () => { + + const textYOffset = sliderStepY * 0.25; + + noStroke(); + fill(0); + textSize(12); + textFont('consolas'); + + textWrap(WORD); + text("Gray Scott vs. Perlin Noise \n\n What happens when reaction diffusion is subjected to external forces?", sliderOffsetX, sliderOffsetX, sliderWidth); + + text("noise scale: " + noiseScalar, sliderOffsetX, titleOffsetY + sliderStepY - textYOffset); + text("noise magnitude: " + vecScalar, sliderOffsetX, titleOffsetY + sliderStepY * 2 - textYOffset); + text("feed rate: " + feedRate, sliderOffsetX, titleOffsetY + sliderStepY * 3 - textYOffset); + text("kill rate: " + killRate, sliderOffsetX, titleOffsetY + sliderStepY * 4 - textYOffset); + text("speed: " + timeScalar, sliderOffsetX, titleOffsetY + sliderStepY * 5 - textYOffset); + text("brush diameter: " + paintRangeScalar, sliderOffsetX, titleOffsetY + sliderStepY * 6 - textYOffset); + text("feed/kill presets", sliderOffsetX, titleOffsetY + sliderStepY * 7 - textYOffset); +} + +drawVecGrid = () => { + + stroke(0); + //line(0, 0, mouseX, mouseY); + + for(let xStep = 0; xStep < gridVec.length; xStep++) + { + const xCtr = uiWidth + (xStep * vecStep) + (vecStep * 0.5); + + //console.log(xCtr); + + for(let yStep = 0; yStep < gridVec[xStep].length; yStep++) + { + const yCtr = (yStep * vecStep) + (vecStep * 0.5); + + //if(xStep == 0) console.log(xCtr + ", " + yCtr); + + const vec = gridVec[xStep][yStep]; + const dX = vec.x * vecStep * vecScalar * 10; + const dY = vec.y * vecStep * vecScalar * 10; + + line(xCtr - dX, yCtr - dY, xCtr + dX, yCtr + dY); + } + } +} + +drawPaintRadius = () =>{ + if(mouseX > uiWidth && mouseX < totalWidth && mouseY > 0 && mouseY < h) + { + noFill(); + stroke(0); + + let dim = w; + if(dim > h) dim = h; + + circle(mouseX, mouseY, paintRangeScalar * dim); + } +} + //replace current grid with future grid step = () => { const toDrawOver = gridNow; @@ -274,25 +499,30 @@ step = () => { //checks for paint input paintCheck = () => { - if(mouseIsPressed) paint(mouseX, mouseY, paintRangeScalar); + if(mouseIsPressed) paint(mouseX, mouseY, paintRangeScalar, 1); //TODO: erase functionality } //applies paint input -paint = (mX, mY, rangeScalar) => { +paint = (mX, mY, rangeScalar, paintOrErase) => { let dim = w; if(dim > h) dim = h; - + + //console.log(mX + ", " + mY); + mX -= uiWidth; + //check if the mouse is completely off the canvas in terms of radius + if(mX < 0 || mX > w || mY < 0 || mY > h) return; + const radius = floor(dim * rangeScalar * 0.5); let x0 = floor(mX - radius); let x1 = floor(mX + radius); let y0 = floor(mY - radius); let y1 = floor(mY + radius); - //check if the mouse is completely off the canvas in terms of radius - if(x1 < 0 || x0 > w || y1 < 0 || y0 > h) return; + // //check if the mouse is completely off the canvas in terms of radius + // if(x1 < 0 || x0 > w || y1 < 0 || y0 > h) return; if(x0 < 0) x0 = 0; if(x1 > w) x1 = w; @@ -308,43 +538,29 @@ paint = (mX, mY, rangeScalar) => { const d = dist(x, y, mX, mY); if(d > radius) continue; - - const vLinear = (radius - d) / radius; - //const vPow = vLinear * vLinear * vLinear * 0.25; - //const vGaussian = exp(vLinear * vLinear * -1); - const vExp = map(exp(vLinear), 0, 15, 0, 1); - const v = vExp; - - if( v > gridNow[x][y].b) gridNow[x][y].b = v; + //else if(d < radius * 0.1) gridNow[x][y].b = 1 * paintOrErase; + else + { + const vLinear = (radius - d) / radius; + //const vPow = vLinear * vLinear * vLinear * 0.25; + //const vGaussian = exp(vLinear * vLinear * -1); + const vExp = map(exp(vLinear), 0, 10, 0, 1); + const v = vExp; + + if( v > gridNow[x][y].b) gridNow[x][y].b = v * paintOrErase; + } } } } -//cell class -class Cell { - constructor(x, y, a, b) - { - this.x = x; - this.y = y; - this.a = a; - this.b = b; - - this.set = (a, b) => - { - this.a = constrain(a, 0, 1); - this.b = constrain(b, 0, 1); - } - } -} - -//pick up here! Build out bias vector UI +// //pick up here! Build out bias vector UI -//TEMP: keyboard bias input for testing -const biasStep = 0.1; -function keyPressed() { - if(key == 'w') updateBias(createVector(0, -biasStep)); - if(key == 'a') updateBias(createVector(-biasStep, 0)); - if(key == 's') updateBias(createVector(0, biasStep)); - if(key == 'd') updateBias(createVector(biasStep, 0)); -} \ No newline at end of file +// //TEMP: keyboard bias input for testing +// const biasStep = 0.1; +// function keyPressed() { +// if(key == 'w') updateBias(createVector(0, -biasStep)); +// if(key == 'a') updateBias(createVector(-biasStep, 0)); +// if(key == 's') updateBias(createVector(0, biasStep)); +// if(key == 'd') updateBias(createVector(biasStep, 0)); +// } \ No newline at end of file diff --git a/public/style/style.css b/public/style/style.css new file mode 100644 index 0000000..8b2c273 --- /dev/null +++ b/public/style/style.css @@ -0,0 +1,23 @@ +body { + font-family: 'consolas'; + background-color: white; + color: black; + /* padding:25%; */ +} + +/* main { + margin: auto; +} */ + +/* #p5Canvas { + margin: auto; +} */ + +/* #mySlider { + position: relative; +} */ + +input { + background-color: white; + color:black +} \ No newline at end of file