elements in my array is undefined, but outside cycle they are good - javascript

Why my elements of arrays P0 and P1 is undefined? They are undefined in cycle "while (run === true)". Outside this cycle they equals 0
function Q(x, y) {
return 10000;
}
function Create2DArray(rows) {
var mas = [];
for (var i = 0; i < rows; i++) {
mas[i] = [];
for (var j = 0; j < rows + 1; j++) {
mas[i][j] = 0;
}
}
return mas
}
let t = 0, tmax = 50, RightX = 250, h = 10, d = 19, K = 0.4, wind = 0;
let N = RightX / h
console.log(N);
C = 5
let alpha = 10 * Math.PI / 180
let U = C * Math.cos(alpha)
let V = C * Math.sin(alpha)
let M1, M2, M3, M4
let D = d * C;
let P0 = Create2DArray(N)
let P1 = Create2DArray(N)
console.log(P1);
let MinVal = h / U
if (MinVal > h / V) {
MinVal = h / V
}
if (MinVal > ((h * h) / (2 * D))) {
MinVal = (h * h) / (2 * D)
}
let tau = K * MinVal
let i = 0, j = 0;
t = t + tau
let run = true
while (run === true) {
for (i = 0; i <= N - 1; i++) {
for (j = 0; j <= N - 1; j++) {
if (U > 0) {
M1 = P0[i][j] * U * h * tau
M2 = P0[i][j - 1] * U * h * tau
}
else if (U < 0) {
M1 = P0[i + 1][j] * U * h * tau
M2 = P0[i][j] * U * h * tau
}
else if (V > 0) {
M3 = P0[i][j] * V * h * tau
M4 = P0[i][j - 1] * V * h * tau
}
else if (V < 0) {
M3 = P0[i][j + 1] * V * h * tau;
M4 = P0[i][j] * V * h * tau;
}
else {
console.log("test");
}
S1 = P0[i][j]
S2 = (tau / (h * h)) * D * (P0[i + 1][j] - 4 * P0[i][j] + P0[i][j] + P0[i][j + 1] + P0[i][j])
S3 = 1 / (h * h) * (M1 - M2 + M3 - M4)
S4 = Q(i, j) * tau
P1[i, j] = S1 - S3 + S2 + S4
}
for (let z = 0; z <= N + 1; z++) {
P1[0, z] = 0;
P1[N + 1, z] = 0;
}
for (let z = 0; z <= N + 1; z++) {
P1[z, 0] = 0;
P1[z, N + 1] = 0;
}
for (let z = 0; z <= N + 1; z++) {
for (let r = 0; r <= N + 1; r++) {
P0[z, r] = P1[z, r]
}
}
}
t = tau + t
if (t > tmax) {
run = false
}
}
I tried to change the methods for creating two-dimensional arrays, I also tried to move the loop and everything worked, however, in such a construction, the elements of the array P0 and P1 are equal to undefined

In javascript the "," operator return the right value.
So when you write PO[z, r], it's like you write PO[r].
I guess you want to write PO[z][r] = P1[z][r] instead.

Related

This code runs for grid<=11 but gets stuck forever for grid>=12. Can anyone explain why this happens? (Using p5.js library of JavaScript)

Basically the title. I have read every line many times and still can't find my mistake.
I am just trying to put squares on a grid by calling a recursive function which creates the object and then calls itself again. I have checked that recursion is not infinite and there's a simple exit condition. Please help.
let grid = 11;
let sqr = [];
function setup() {
createCanvas(grid * grid, grid * grid);
noFill();
colorMode(HSB);
noLoop();
let maxs = floor(grid / 3);
let ratio = 2 * maxs * maxs;
makegrid(maxs, ratio);
}
function draw() {
background(0);
for (let sq of sqr) sq.show();
}
function makegrid(m, r) {
if (!m) return;
if (m == floor(grid / 3)) {
for (let i = 0; i < 2; i++) sqr.push(new sqrs(m, r));
m--;
makegrid(m, r);
} else {
let j = r / (m * m);
for (let k = 0; k < j; k++) sqr.push(new sqrs(m, r));
m--;
makegrid(m, r);
}
}
class sqrs {
constructor(m, r) {
let flag = true;
this.s = (m * width) / grid;
while (flag) {
flag = false;
this.x = (width / grid) * floor((grid + 1 - m) * random());
this.y = (height / grid) * floor((grid + 1 - m) * random());
if (!sqr.length) flag = false;
else {
for (let sq of sqr) {
let d = (this.x - sq.x) ** 2 + (this.y - sq.y) ** 2;
if (d < this.s ** 2 || d < sq.s ** 2) {
flag = true;
break;
}
}
}
}
}
show() {
stroke(random(340), 80, 80);
square(this.x, this.y, this.s);
}
}
As Jay pointed out in the comments, it's not the recursion that is the problem, but the while (flag) loop in your sqrs constructor that is the problem:
let grid = 12;
let sqr = [];
function setup() {
createCanvas(grid * grid, grid * grid);
noFill();
colorMode(HSB);
noLoop();
// maxs will be 4 if grid is 12
let maxs = floor(grid / 3);
// ratio will be 32
let ratio = 2 * maxs * maxs;
makegrid(maxs, ratio);
}
function draw() {
background(0);
for (let sq of sqr) sq.show();
}
function makegrid(m, r) {
if (m <= 0) return;
if (m == floor(grid / 3)) {
// Call 0: m == floor(grid / 3) == 4
for (let i = 0; i < 2; i++) sqr.push(new sqrs(m, r));
m--;
// Call 0: makegrid(3, 32);
makegrid(m, r);
} else {
// Call 1: j = 32 / (3 * 3) = 3.55555
// Call 2: j = 32 / (2 * 2) = 8
// Call 3: j = 32 / (1 * 1) = 32
let j = r / (m * m);
for (let k = 0; k < j; k++) sqr.push(new sqrs(m, r));
m--;
// Call 1: makegrid(2, 32)
// Call 2: makegrid(1, 32)
// Call 3: makegrid(0, 32)
makegrid(m, r);
}
}
class sqrs {
constructor(m, r) {
let flag = true;
this.s = (m * width) / grid;
// This code might end up repeating forever because every randomly generated
// position is too close to some existing square
let count = 0;
while (flag && ++count < 1000) {
flag = false;
this.x = (width / grid) * floor((grid + 1 - m) * random());
this.y = (height / grid) * floor((grid + 1 - m) * random());
if (sqr.length) {
// Check if the new square is too close to any existing squares
for (let sq of sqr) {
let d = (this.x - sq.x) ** 2 + (this.y - sq.y) ** 2;
if (d < this.s ** 2 || d < sq.s ** 2) {
flag = true;
break;
}
}
}
}
if (flag) {
this.s = 0;
print(`gave up after ${count} attempts to find a position for this square.`);
}
}
show() {
stroke(random(340), 80, 80);
square(this.x, this.y, this.s);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
The logic in that while loop doesn't make a whole lot of sense to me, so you're going to have to describe what you are actually trying to do if you want more advice.

Why does my object keep getting NaN values?

I´ve been messing around with a library called p5.js and I encounter a problem while trying to modify the values of an object. After creating the array and pushing the objects I check for the object and it has many NaN values. This code can be run in editor.p5js.org for free in case you need it. Thanks in advance.
function setup() {
createCanvas(600, 600);
background(0);
p = [];
planetnum = 3; //Set one more
for (u = 0; u < planetnum; u++) {
p.push({
rx: 0,
ry: 0,
vx: 0,
vy: 0,
ax: 0,
ay: 0,
m: 10
});
}
for (u = 0; u < planetnum; u++) {
p[u].rx = 0;
p[u].ry = 0;
p[u].vx = 0;
p[u].vy = 0;
p[u].ax = 0;
p[u].ay = 0;
p[u].m = 10;
}
p[0].ry = 100;
p[1].rx = 100;
p[2].rx = -100;
console.log(p[1]);
}
function draw() {
background(0);
translate(width / 2, height / 2);
scale(1, -1);
console.log("Working");
color(255);
//Calculate next position
for (i = 0; i < planetnum; i++) {
p[i].ax = 0;
p[i].ay = 0;
console.log(p[i]);
for (o = 0; o < planetnum; o++) {
if (o != i) {
d = sqrt((p[i].ry - p[o].ry) ^ 2 + (p[i].rx - p[o].rx) ^ 2);
f = -0.001 / (d * d);
angle = Math.atan2((p[i].ry - p[o].ry), (p[i].rx - p[o].rx));
p[i].ax += f * d * Math.cos(angle) / p[i].m;
p[i].ay += f * d * Math.sin(angle) / p[i].m;
}
}
p[i].vx += p[i].ax;
p[i].ay += p[i].ay;
p[i].rx += p[i].vx;
p[i].ry += p[i].vy;
}
//Draw circles
for (i = 0; i < planetnum; i++) {
circle(p[i].rx, p[i].ry, 10);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
I'm guessing that when you wrote this code:
d = sqrt((p[i].ry - p[o].ry) ^ 2 + (p[i].rx - p[o].rx) ^ 2);
Your intention was to square each of those deltas. However, in JavaScript ^ is the bitwise XOR operator, not the exponent operator. This is probably resulting in your taking the square root of -1 which is NaN. This fixes the NaN problem:
d = sqrt((p[i].ry - p[o].ry) ** 2 + (p[i].rx - p[o].rx) ** 2);
Note: on certain defunct browsers you might need to use Math.pow(p[i].ry - p[o].ry, 2), also you could consider the p5.js dist function: dist(p[i].rx, p[i].ry, p[o].rx, p[o].ry).
function setup() {
createCanvas(600, 600);
background(0);
p = [];
planetnum = 3; //Set one more
for (u = 0; u < planetnum; u++) {
p.push({
rx: 0,
ry: 0,
vx: 0,
vy: 0,
ax: 0,
ay: 0,
m: 10
});
}
for (u = 0; u < planetnum; u++) {
p[u].rx = 0;
p[u].ry = 0;
p[u].vx = 0;
p[u].vy = 0;
p[u].ax = 0;
p[u].ay = 0;
p[u].m = 10;
}
p[0].ry = 100;
p[1].rx = 100;
p[2].rx = -100;
console.log(p[1]);
}
function mouseClicked() {
console.log("Debug:");
for (i = 0; i < planetnum; i++) {
console.log(p[i]);
}
}
function draw() {
background(0);
translate(width / 2, height / 2);
scale(1, -1);
color(255);
//Calculate next position
for (i = 0; i < planetnum; i++) {
p[i].ax = 0;
p[i].ay = 0;
for (o = 0; o < planetnum; o++) {
if (o != i) {
d = sqrt((p[i].ry - p[o].ry) ** 2 + (p[i].rx - p[o].rx) ** 2);
f = -0.001 / (d * d);
angle = Math.atan2((p[i].ry - p[o].ry), (p[i].rx - p[o].rx));
p[i].ax += f * d * Math.cos(angle) / p[i].m;
p[i].ay += f * d * Math.sin(angle) / p[i].m;
}
}
p[i].vx += p[i].ax;
p[i].ay += p[i].ay;
p[i].rx += p[i].vx;
p[i].ry += p[i].vy;
}
//Draw circles
for (i = 0; i < planetnum; i++) {
circle(p[i].rx, p[i].ry, 10);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

Custom marker icons color

Is there any new API to create custom icons given a color and text? I'd like to send an hex color.
I've been using a couple of URL to generate my markers icons but now it seems to be deprecated.
I have not been able to find a new one
There is my old function:
function getIcon(text, fillColor, textColor, outlineColor) {
if (!text) text = '•'; //generic map dot
var iconUrl = "https://chart.googleapis.com/chart?chst=d_map_pin_letter&chld=" + text + "|" + fillColor;
//var iconUrl = "http://chart.googleapis.com/chart?cht=d&chdp=mapsapi&chl=pin%27i\\%27[" + text + "%27-2%27f\\hv%27a\\]h\\]o\\" + fillColor + "%27fC\\" + textColor + "%27tC\\" + outlineColor + "%27eC\\Lauto%27f\\&ext=.png";
return iconUrl;
}
Thanks in advance!
If you are open to put in some code here is a link which can convert an image, in your case the marker to a desired colour.
Codepen Link
'use strict';
class Color {
constructor(r, g, b) {
this.set(r, g, b);
}
toString() {
return `rgb(${Math.round(this.r)}, ${Math.round(this.g)}, ${Math.round(this.b)})`;
}
set(r, g, b) {
this.r = this.clamp(r);
this.g = this.clamp(g);
this.b = this.clamp(b);
}
hueRotate(angle = 0) {
angle = angle / 180 * Math.PI;
const sin = Math.sin(angle);
const cos = Math.cos(angle);
this.multiply([
0.213 + cos * 0.787 - sin * 0.213,
0.715 - cos * 0.715 - sin * 0.715,
0.072 - cos * 0.072 + sin * 0.928,
0.213 - cos * 0.213 + sin * 0.143,
0.715 + cos * 0.285 + sin * 0.140,
0.072 - cos * 0.072 - sin * 0.283,
0.213 - cos * 0.213 - sin * 0.787,
0.715 - cos * 0.715 + sin * 0.715,
0.072 + cos * 0.928 + sin * 0.072,
]);
}
grayscale(value = 1) {
this.multiply([
0.2126 + 0.7874 * (1 - value),
0.7152 - 0.7152 * (1 - value),
0.0722 - 0.0722 * (1 - value),
0.2126 - 0.2126 * (1 - value),
0.7152 + 0.2848 * (1 - value),
0.0722 - 0.0722 * (1 - value),
0.2126 - 0.2126 * (1 - value),
0.7152 - 0.7152 * (1 - value),
0.0722 + 0.9278 * (1 - value),
]);
}
sepia(value = 1) {
this.multiply([
0.393 + 0.607 * (1 - value),
0.769 - 0.769 * (1 - value),
0.189 - 0.189 * (1 - value),
0.349 - 0.349 * (1 - value),
0.686 + 0.314 * (1 - value),
0.168 - 0.168 * (1 - value),
0.272 - 0.272 * (1 - value),
0.534 - 0.534 * (1 - value),
0.131 + 0.869 * (1 - value),
]);
}
saturate(value = 1) {
this.multiply([
0.213 + 0.787 * value,
0.715 - 0.715 * value,
0.072 - 0.072 * value,
0.213 - 0.213 * value,
0.715 + 0.285 * value,
0.072 - 0.072 * value,
0.213 - 0.213 * value,
0.715 - 0.715 * value,
0.072 + 0.928 * value,
]);
}
multiply(matrix) {
const newR = this.clamp(this.r * matrix[0] + this.g * matrix[1] + this.b * matrix[2]);
const newG = this.clamp(this.r * matrix[3] + this.g * matrix[4] + this.b * matrix[5]);
const newB = this.clamp(this.r * matrix[6] + this.g * matrix[7] + this.b * matrix[8]);
this.r = newR;
this.g = newG;
this.b = newB;
}
brightness(value = 1) {
this.linear(value);
}
contrast(value = 1) {
this.linear(value, -(0.5 * value) + 0.5);
}
linear(slope = 1, intercept = 0) {
this.r = this.clamp(this.r * slope + intercept * 255);
this.g = this.clamp(this.g * slope + intercept * 255);
this.b = this.clamp(this.b * slope + intercept * 255);
}
invert(value = 1) {
this.r = this.clamp((value + this.r / 255 * (1 - 2 * value)) * 255);
this.g = this.clamp((value + this.g / 255 * (1 - 2 * value)) * 255);
this.b = this.clamp((value + this.b / 255 * (1 - 2 * value)) * 255);
}
hsl() {
// Code taken from https://stackoverflow.com/a/9493060/2688027, licensed under CC BY-SA.
const r = this.r / 255;
const g = this.g / 255;
const b = this.b / 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return {
h: h * 100,
s: s * 100,
l: l * 100,
};
}
clamp(value) {
if (value > 255) {
value = 255;
} else if (value < 0) {
value = 0;
}
return value;
}
}
class Solver {
constructor(target, baseColor) {
this.target = target;
this.targetHSL = target.hsl();
this.reusedColor = new Color(0, 0, 0);
}
solve() {
const result = this.solveNarrow(this.solveWide());
return {
values: result.values,
loss: result.loss,
filter: this.css(result.values),
};
}
solveWide() {
const A = 5;
const c = 15;
const a = [60, 180, 18000, 600, 1.2, 1.2];
let best = { loss: Infinity };
for (let i = 0; best.loss > 25 && i < 3; i++) {
const initial = [50, 20, 3750, 50, 100, 100];
const result = this.spsa(A, a, c, initial, 1000);
if (result.loss < best.loss) {
best = result;
}
}
return best;
}
solveNarrow(wide) {
const A = wide.loss;
const c = 2;
const A1 = A + 1;
const a = [0.25 * A1, 0.25 * A1, A1, 0.25 * A1, 0.2 * A1, 0.2 * A1];
return this.spsa(A, a, c, wide.values, 500);
}
spsa(A, a, c, values, iters) {
const alpha = 1;
const gamma = 0.16666666666666666;
let best = null;
let bestLoss = Infinity;
const deltas = new Array(6);
const highArgs = new Array(6);
const lowArgs = new Array(6);
for (let k = 0; k < iters; k++) {
const ck = c / Math.pow(k + 1, gamma);
for (let i = 0; i < 6; i++) {
deltas[i] = Math.random() > 0.5 ? 1 : -1;
highArgs[i] = values[i] + ck * deltas[i];
lowArgs[i] = values[i] - ck * deltas[i];
}
const lossDiff = this.loss(highArgs) - this.loss(lowArgs);
for (let i = 0; i < 6; i++) {
const g = lossDiff / (2 * ck) * deltas[i];
const ak = a[i] / Math.pow(A + k + 1, alpha);
values[i] = fix(values[i] - ak * g, i);
}
const loss = this.loss(values);
if (loss < bestLoss) {
best = values.slice(0);
bestLoss = loss;
}
}
return { values: best, loss: bestLoss };
function fix(value, idx) {
let max = 100;
if (idx === 2 /* saturate */) {
max = 7500;
} else if (idx === 4 /* brightness */ || idx === 5 /* contrast */) {
max = 200;
}
if (idx === 3 /* hue-rotate */) {
if (value > max) {
value %= max;
} else if (value < 0) {
value = max + value % max;
}
} else if (value < 0) {
value = 0;
} else if (value > max) {
value = max;
}
return value;
}
}
loss(filters) {
// Argument is array of percentages.
const color = this.reusedColor;
color.set(0, 0, 0);
color.invert(filters[0] / 100);
color.sepia(filters[1] / 100);
color.saturate(filters[2] / 100);
color.hueRotate(filters[3] * 3.6);
color.brightness(filters[4] / 100);
color.contrast(filters[5] / 100);
const colorHSL = color.hsl();
return (
Math.abs(color.r - this.target.r) +
Math.abs(color.g - this.target.g) +
Math.abs(color.b - this.target.b) +
Math.abs(colorHSL.h - this.targetHSL.h) +
Math.abs(colorHSL.s - this.targetHSL.s) +
Math.abs(colorHSL.l - this.targetHSL.l)
);
}
css(filters) {
function fmt(idx, multiplier = 1) {
return Math.round(filters[idx] * multiplier);
}
return `filter: invert(${fmt(0)}%) sepia(${fmt(1)}%) saturate(${fmt(2)}%) hue-rotate(${fmt(3, 3.6)}deg) brightness(${fmt(4)}%) contrast(${fmt(5)}%);`;
}
}
function hexToRgb(hex) {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, (m, r, g, b) => {
return r + r + g + g + b + b;
});
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16),
]
: null;
}
$(document).ready(() => {
$('button.execute').click(() => {
const rgb = hexToRgb($('input.target').val());
if (rgb.length !== 3) {
alert('Invalid format!');
return;
}
const color = new Color(rgb[0], rgb[1], rgb[2]);
const solver = new Solver(color);
const result = solver.solve();
let lossMsg;
if (result.loss < 1) {
lossMsg = 'This is a perfect result.';
} else if (result.loss < 5) {
lossMsg = 'The is close enough.';
} else if (result.loss < 15) {
lossMsg = 'The color is somewhat off. Consider running it again.';
} else {
lossMsg = 'The color is extremely off. Run it again!';
}
$('.realPixel').css('background-color', color.toString());
$('.filterPixel').attr('style', result.filter);
$('.filterDetail').text(result.filter);
$('.lossDetail').html(`Loss: ${result.loss.toFixed(1)}. <b>${lossMsg}</b>`);
});
});
You can then send this filter output to CSS of your marker and it will change the colour of the image

Colorize or replace the color of a textured image by a specified color using javascript and HTML5 canvas.Like color blend mode in PS

I have a textured image with color. What i want is to change the color of the image but retaining its texture. i already know how to get the data of an image,pixel by pixel(the rgb values).
here's the sample image:
i want it to be colored into these:
Here's an interactive solution. Run it from localhost and specify a valid image.
<!DOCTYPE html>
<html>
<head>
<script>
function byId(e){return document.getElementById(e);}
function newEl(tag){return document.createElement(tag);}
function newTxt(txt){return document.createTextNode(txt);}
window.addEventListener('load', mInit, false);
function mInit()
{
var srcImg = byId('srcImg');
byId('hueSlider').style.width = srcImg.width + "px";
var destImg = byId('dstImg');
destImg.width = srcImg.width;
destImg.height = srcImg.height;
}
function colorize()
{
var curHue = byId('hueSlider').value;
var curSat = byId('satSlider').value;// / 100.0;
var curLum = byId('lumSlider').value / 100.0;
byId('hueVal').innerHTML = curHue;
byId('satVal').innerHTML = curSat;
byId('lumVal').innerHTML = curLum;
var dst = byId('dstImg');
var dstCtx = dst.getContext('2d');
var img = byId('srcImg');
dstCtx.drawImage(img, 0, 0);
var dstImgData = dstCtx.getImageData(0,0,dst.width,dst.height);
var i, j, r,g,b,hsl,rgb, index;
if (byId('colCheckBox').checked)
{
console.log('colourizing');
for (j=0; j<dst.height; j++)
{
for (i=0; i<dst.width; i++)
{
index = (i + j*dst.width) * 4; // 4 bytes/pixel, set index to point to r component of x,y in dst image
r = dstImgData.data[index+0];
g = dstImgData.data[index+1];
b = dstImgData.data[index+2];
hsl = rgb2hsl (r, g, b);
hsl.h = curHue;
//if (hsl.h > 359)
// hsl.h -= 360;
hsl.s = curSat;
hsl.l *= curLum;
rgb = hsl2rgb(hsl.h, hsl.s, hsl.l);
dstImgData.data[index+0] = rgb.r;
dstImgData.data[index+1] = rgb.g;
dstImgData.data[index+2] = rgb.b;
}
}
dstCtx.putImageData(dstImgData,0,0);
}
}
// code taken from nichabi.com
function hsl2rgb (h, s, l) {
var r, g, b, m, c, x
if (!isFinite(h)) h = 0
if (!isFinite(s)) s = 0
if (!isFinite(l)) l = 0
h /= 60
if (h < 0) h = 6 - (-h % 6)
h %= 6
s = Math.max(0, Math.min(1, s / 100))
l = Math.max(0, Math.min(1, l / 100))
c = (1 - Math.abs((2 * l) - 1)) * s
x = c * (1 - Math.abs((h % 2) - 1))
if (h < 1) {
r = c
g = x
b = 0
} else if (h < 2) {
r = x
g = c
b = 0
} else if (h < 3) {
r = 0
g = c
b = x
} else if (h < 4) {
r = 0
g = x
b = c
} else if (h < 5) {
r = x
g = 0
b = c
} else {
r = c
g = 0
b = x
}
m = l - c / 2
r = Math.round((r + m) * 255)
g = Math.round((g + m) * 255)
b = Math.round((b + m) * 255)
return { r: r, g: g, b: b }
}
// code taken from nichabi.com
function rgb2hsl (r, g, b)
{
var max, min, h, s, l, d
r /= 255
g /= 255
b /= 255
max = Math.max(r, g, b)
min = Math.min(r, g, b)
l = (max + min) / 2
if (max == min) {
h = s = 0
} else {
d = max - min
s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0)
break
case g:
h = (b - r) / d + 2
break
case b:
h = (r - g) / d + 4
break
}
h /= 6
}
h = Math.floor(h * 360)
s = Math.floor(s * 100)
l = Math.floor(l * 100)
return { h: h, s: s, l: l }
}
</script>
<style>
body
{
background: #888;
}
#hueVal, #satVal, #lumVal
{
font-size: 0.8em;
font-weight: bold;
display: inline;
}
</style>
</head>
<body>
<img id='srcImg' src='handlebars.png'/><canvas id='dstImg'></canvas>
<br>
<label><input type='checkbox' id='colCheckBox' onchange='colorize()' />Colourize</label>
<br>
Hue<input id='hueSlider' onchange='colorize()' type='range' min='0' max='359'/><div id='hueVal'></div><br>
Saturation<input id='satSlider' onchange='colorize()' type='range' value='50' min='0' max='100'/><div id='satVal'></div><br>
Luminance<input id='lumSlider' onchange='colorize()' type='range' min='0' max='200'/><div id='lumVal'></div>
</body>
</html>

I cannot generate smooth Simplex noise in Javascript

I've tried everything and read every single link I can see on the internet regarding Perlin Noise or Simplex Noise and even dissected a few Javascript examples that I see work fine.
But I still get very random looking images... essentially just TV static.
My code is below. I'm using a random number generator so that I can seed a value, but I've tried with Math.random as well.
As near as I can tell, the different images generated at the different octaves aren't interpolating properly, or maybe the way I'm converting from the Noise function to RGB values is wrong (I've tried to fix both of these issues...).
if (!this.Prng) {
var Prng = function() {
var iMersenne = 2147483647;
var rnd = function(seed) {
if (arguments.length) {
that.seed = arguments[0];
}
that.seed = that.seed*16807%iMersenne;
return that.seed;
};
var that = {
seed: 123,
rnd: rnd,
random: function(seed) {
if (arguments.length) {
that.seed = arguments[0];
}
return rnd()/iMersenne;
}
};
return that;
}();
}
var CSimplexNoise = function(r)
{
this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0],[1,0,1],[-1,0,1],
[1,0,-1],[-1,0,-1],[0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]];
var p = [];
for(i = 0; i < 256; i++)
p[i] = Math.floor(r.random()*256);
this.perm = new Array();
for(i = 0; i < 512; i++)
{
this.perm[i] = p[i & 255];
}
}
CSimplexNoise.prototype.dot = function(g,x,y)
{
return g[0]*x + g[1]*y;
}
CSimplexNoise.prototype.GenerateSimplexNoise = function(x,y,octaves,persistence)
{
var total = 0;
for(i=0; i < octaves-1; i++)
{
var freq = Math.pow(2,i);
var amp = Math.pow(persistence,i);
total += this.InterpolatedNoise(x*freq,y*freq) * amp;
}
return total;
}
CSimplexNoise.prototype.InterpolatedNoise = function(x,y)
{
var xInt = Math.floor(x);
var xFrac = x - xInt;
var yInt = Math.floor(y);
var yFrac = y - yInt;
var v1 = this.SmoothNoise(xInt,yInt);
var v2 = this.SmoothNoise(xInt + 1,yInt)
var v3 = this.SmoothNoise(xInt,yInt+1)
var v4 = this.SmoothNoise(xInt + 1, yInt + 1);
var i1 = this.LinearInterpolate(v1,v2,xFrac);
var i2 = this.LinearInterpolate(v3,v4,xFrac);
return this.CosineInterpolate(i1,i2,yFrac);
}
CSimplexNoise.prototype.LinearInterpolate = function(a,b,x)
{
return a*(1-x) + b*x;
}
CSimplexNoise.prototype.CosineInterpolate = function(a,b,x)
{
var f = (1 - Math.cos(x*Math.PI)) * 0.5;
return a*(1-f) + b*f;
}
CSimplexNoise.prototype.SmoothNoise = function(x,y)
{
var corners = (this.Noise(x-1,y-1) + this.Noise(x+1,y-1) + this.Noise(x-1,y+1) + this.Noise(x+1,y+1)) / 16;
var sides = (this.Noise(x-1,y) + this.Noise(x+1,y) + this.Noise(x,y-1) + this.Noise(x+1,y+1)) / 8;
var center = this.Noise(x,y) / 4;
return corners + sides + center;
}
CSimplexNoise.prototype.Noise = function(xin, yin)
{
var n0, n1, n2;
var F2 = 0.5*(Math.sqrt(3)-1);
var s = (xin+yin)*F2;
var i = Math.floor(xin+s);
var j = Math.floor(yin+s);
var G2 = (3-Math.sqrt(3))/6;
var t = (i+j)*G2;
var X0 = i-t;
var Y0 = j-t;
var x0 = xin-X0;
var y0 = yin-Y0;
var i1,j1;
if(x0 > y0)
{
i1 = 1;
j1 = 0;
}
else
{
i1 = 0;
j1 = 1;
}
var x1 = x0 - i1 + G2;
var y1 = y0 - j1 + G2;
var x2 = x0 - 1 + 2 * G2;
var y2 = y0 - 1 + 2 * G2;
var ii = i & 255;
var jj = j & 255;
var gi0 = this.perm[ii + this.perm[jj]] % 12;
var gi1 = this.perm[ii + i1 + this.perm[jj + j1]] % 12;
var gi2 = this.perm[ii + 1 + this.perm[jj + 1]] % 12;
var t0 = 0.5 - x0 * x0 - y0 * y0;
if(t0 < 0)
n0 = 0;
else
{
t0 *= t0;
n0 = t0 * t0 * this.dot(this.grad3[gi0],x0,y0)
}
var t1 = 0.5 - x1 * x1 - y1 * y1;
if(t1 < 0)
n1 = 0;
else
{
t1 *= t1;
n1 = t1 * t1 * this.dot(this.grad3[gi1],x1,y1);
}
var t2 = 0.5 - x2 * x2 - y2 * y2;
if(t2 <0 )
n2 = 0;
else
{
t2 *= t2;
n2 = t2 * t2 * this.dot(this.grad3[gi2],x2,y2);
}
return 70 * (n0 + n1 + n2);
}
$(document).ready(function(){
var context = $('#screen')[0].getContext("2d");
var w = 100;
var h = 100;
var data = context.createImageData(w,h);
var simplexNoise = new CSimplexNoise(Prng);
for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
// var newVal = ((simplexNoise.GenerateSimplexNoise(x,y,5,0.25) - -1) / (1 - -1)) * (255 - 0);
var newVal2 = simplexNoise.GenerateSimplexNoise(x,y,5,0.5)
var newVal = Math.floor(newVal2*256);
newVal = Math.abs(newVal * 2)-0.5;
data.data[((h * y) + x) * 4] = newVal;
data.data[((h * y) + x) * 4+1] = newVal;
data.data[((h * y) + x) * 4+2] = newVal;
data.data[((h * y) + x) * 4+3] = 255;
}
}
context.putImageData(data,0,0);
})
Try sampling simplexNoise.GenerateSimplexNoise(x * 0.05, y * 0.05, 5, 0.5)
The problem may be that your samples are too far apart. (this would result in apparently random behavior, since the simplex noise might go through more than half a wavelength before you sample it)
REVISION: Updated numbers above...
You may actually need to reduce the samples so that there are 20 in a given wavelength of the simplex noise. The average wavelength of most simplex noise is 1, so 0.05 should do the trick. Also, you may want to test with just one octave at first.

Categories

Resources