Why does this canvas animation occasionally scramble itself? - javascript

For a website I am creating, I coded a parallax background using HTML5 canvas. Running the snippet below will produce a 192x192 sample of the background animation.
function parallaxCipher(size, color, speed, grid, sector, seed){
var scr = document.getElementById("parallax" + sector);
var ctx = scr.getContext("2d");
ctx.fillStyle = "#000";
ctx.fillRect(0,0,scr.width,scr.height);
var xGridMax = Math.ceil(scr.width / grid);
var yGridMax = Math.ceil(scr.height / grid) + 1;
var seedList = prng(seed,yGridMax * 2);
for(var c = 0; c < yGridMax;c++){
seedList[c] += seedList[c + yGridMax] * 256;
}
seedList.length = yGridMax;
var rotation = Math.floor(Math.floor(Date.now() / speed) / grid) % yGridMax;
rotation = yGridMax - rotation - 1;
if(rotation > 0){
seedList = seedList.slice(seedList.length-rotation).concat(seedList.slice(0,seedList.length - rotation));
}
ctx.fillStyle = color;
for(var a = 0;a<yGridMax;a++){
var row = prng(seedList[a],Math.ceil((xGridMax + 1)/8));
var row2 = [];
for(var d = 0;d<row.length;d++){
row[d] = row[d].toString(2);
row[d] = "0".repeat(8 - row[d].length) + row[d];
row2 = row2.concat(row[d].split(''));
}
row2.length = xGridMax + 1;
var rotation2 = Math.floor(Math.floor(Date.now() / speed) / grid) % xGridMax;
if(rotation2 > 0){
row2 = row2.slice(row2.length-rotation2).concat(row2.slice(0,row2.length - rotation2));
}
for(var b = -1;b<xGridMax;b++){
ctx.font = size + "px monospace";
ctx.fillText(row2[b + 1],(b * grid) - (size * 11 / 30) + ((Date.now() / speed) % grid),(a * grid) + (size / 2) - ((Date.now() / speed) % grid));
}
}
}
function prng(seed, instances){
//PRNG takes a 16 bit seed
var primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597,1601,1607,1609,1613,1619,1621,1627];
var res = [];
var j = seed % 256;
var k = Math.floor(seed / 256);
for(var a = 0;a < instances;a++){
res.push(((primes[j] * primes[k]) % 257) - 1);
if(primes.includes(res[res.length - 1]) || primes.includes(res[res.length - 1] + 1)){
j = (j + 1) % 257;
}else{
k = (k - 1);
if(k == -1){
k = 256;
}
}
if(res[res.length - 1] == undefined){
console.warn("PRNG error: " + j + ", " + k + ", " + primes[j] + ", " + primes[k]);
}
}
return res;
}
function animate(){
window.requestAnimationFrame(animate);
parallaxCipher(15,"#0f0",25,18,"1",9999);
}
window.requestAnimationFrame(animate);
<canvas width="192" height="192" style="border:2px solid black;" id="parallax1">Nope.</canvas>
You may notice that every 10 seconds or so, the bits scramble themselves, as if the seed value randomly changed, even though the seed in this example is fixed at 9999.
I think it may be a problem with the rotation/rotation2 values, but I am not entirely sure. In fact, the bug appears to not be in sync with the rotation cycle at all, leaving me at a complete loss as to how this problem is occuring.
Forgive my terrible code, but can anyone help pinpoint the problem? Any help is appreciated!

I figured it out!
rotation2 is supposed to be calculated modulo xGridMax + 1, not xGridMax.
Thus, the correct animation (large version) is:
function parallaxCipher(size, color, speed, grid, sector, seed){
var scr = document.getElementById("parallax" + sector);
var ctx = scr.getContext("2d");
ctx.fillStyle = "#000";
ctx.fillRect(0,0,scr.width,scr.height);
var xGridMax = Math.ceil(scr.width / grid);
var yGridMax = Math.ceil(scr.height / grid) + 1;
var seedList = prng(seed,yGridMax * 2);
for(var c = 0; c < yGridMax;c++){
seedList[c] += seedList[c + yGridMax] * 256;
}
seedList.length = yGridMax;
var rotation = Math.floor(Math.floor(Date.now() / speed) / grid) % yGridMax;
rotation = yGridMax - rotation - 1;
if(rotation > 0){
seedList = seedList.slice(seedList.length-rotation).concat(seedList.slice(0,seedList.length - rotation));
}
ctx.fillStyle = color;
for(var a = 0;a<yGridMax;a++){
var row = prng(seedList[a],Math.ceil((xGridMax + 1)/8));
var row2 = [];
for(var d = 0;d<row.length;d++){
row[d] = row[d].toString(2);
row[d] = "0".repeat(8 - row[d].length) + row[d];
row2 = row2.concat(row[d].split(''));
}
row2.length = xGridMax + 1;
var rotation2 = Math.floor(Math.floor(Date.now() / speed) / grid) % (xGridMax+1);
if(rotation2 > 0){
row2 = row2.slice(row2.length-rotation2).concat(row2.slice(0,row2.length - rotation2));
}
for(var b = -1;b<xGridMax;b++){
ctx.font = size + "px monospace";
ctx.fillText(row2[b + 1],(b * grid) - (size * 11 / 30) + ((Date.now() / speed) % grid),(a * grid) + (size / 2) - ((Date.now() / speed) % grid));
}
}
}
function prng(seed, instances){
//PRNG takes a 16 bit seed
var primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597,1601,1607,1609,1613,1619,1621,1627];
var res = [];
var j = seed % 256;
var k = Math.floor(seed / 256);
for(var a = 0;a < instances;a++){
res.push(((primes[j] * primes[k]) % 257) - 1);
if(primes.includes(res[res.length - 1]) || primes.includes(res[res.length - 1] + 1)){
j = (j + 1) % 257;
}else{
k = (k - 1);
if(k == -1){
k = 256;
}
}
if(res[res.length - 1] == undefined){
console.warn("PRNG error: " + j + ", " + k + ", " + primes[j] + ", " + primes[k]);
}
}
return res;
}
function animate(){
window.requestAnimationFrame(animate);
parallaxCipher(15,"#0f0",25,18,"1",9999);
}
window.requestAnimationFrame(animate);
<canvas width="512" height="512" style="border:2px solid black;" id="parallax1">Nope.</canvas>

Related

p5.js replace let url to local link

Im noob with p5j and i need help from someone please! :-(
On the website i must upload it , they not allow the external links.
and i need to change the "let url" to a png/jpg local link ( not external link ).
i dont know what the solution is ( maybe something similar with: "loadImage" or something.... please help me :-) )
Thank you so much and have a blessed week!
Here is the code:
//let url = "https://coolors.co/3a2e39-1e555c-f4d8cd-edb183-f15152";
let url = "https://blog.logrocket.com/wp-content/uploads/2022/03/Creating-animations-p5-js.png";
let palette;
let font;
function preload() {
font = loadFont("https://openprocessing.org/sketch/1359269/files/Happy Monsters.ttf");
}
function setup() {
createCanvas(1112, 834);
colorMode(HSB, 360, 100, 100, 100);
angleMode(DEGREES);
palette = createPalette(url);
background(10);
}
function draw() {
//background(0, 0, 90);
let offset = 100//width / 100;
let margin = 0; //offset / 5;
let cells = 1//int(random(2, 8));
let d = (width - offset * 2 - margin * (cells - 1)) / cells;
for (let j = 0; j < cells; j++) {
for (let i = 0; i < cells; i++) {
let x = offset + i * (d + margin) + d / 2;
let y = offset + j * (d + margin) + d / 2;
drawFancyShape(x, y, d, palette.concat());
}
}
frameRate(0.5);
noLoop();
}
function drawFancyShape(x, y, d, colors, char = String.fromCodePoint(65 + int(random(26)))) {
let g = createGraphics(d, d);
let g2 = createGraphics(d, d);
colors = shuffle(colors);
let c0 = colors[0];
colors.splice(0, 1);
let ratio = 0.2;
let xStep, yStep;
for (let y = 0; y < g.height; y += yStep) {
yStep = random(ratio, 1 - ratio) * g.height / 2;
if (y + yStep > g.height) yStep = g.height - y;
if (g.height - y - yStep < g.height / 100) yStep = g.height - y;
for (let x = 0; x < g.width; x += xStep) {
xStep = random(ratio, 1 - ratio) * g.width / 2;
if (x + xStep > g.width) xStep = g.width - x;
if (g.width - x - xStep < g.width / 100) xStep = g.width - x;
let r = [];
for (let i = 0; i < 4; i++) {
r.push(int(random(5)) * max(xStep, yStep) / 4);
}
g.rectMode(CENTER);
g.fill(random(colors));
g.noStroke();
g.rect(x + xStep / 2, y + yStep / 2, xStep - 2, yStep - 2, r[0], r[1], r[2], r[3]);
}
}
g2.textSize(g.width * 0.6);
g2.textAlign(CENTER, CENTER);
g2.textFont(font)
g2.textStyle(BOLD);
g2.fill(c0);
// g2.stroke(0);
g2.text(char, g.width / 2, g.height / 2 - g.height / 8);
let g_tmp = g.get();
let g2_tmp = g2.get();
g_tmp.mask(g2_tmp);
// g_tmp.mask(g2_tmp);
drawingContext.shadowColor = color(0, 0, 0, 33);
drawingContext.shadowBlur = d / 10;
push();
translate(x, y);
imageMode(CENTER);
// image(g, 0, 0);
let scl = 1.1;
image(g2, 0, 0, g2.width * scl, g2.height * scl);
image(g_tmp, 0, 0, g_tmp.width * scl, g_tmp.height * scl);
pop();
}
function createPalette(_url) {
let slash_index = _url.lastIndexOf('/');
let pallate_str = _url.slice(slash_index + 1);
let arr = pallate_str.split('-');
for (let i = 0; i < arr.length; i++) {
arr[i] = color('#' + arr[i]);
}
return arr;
}
// save jpg
let lapse = 0; // mouse timer
function mousePressed(){
// prevents mouse press from registering twice
if (millis() - lapse > 400){
save("img_" + month() + '-' + day() + '_' + hour() + '-' + minute() + '-' + second() + ".jpg");
lapse = millis();
}
}

Duotone image in Canvas

Alright. I'm new to this, I'm not coder, just trying something for fun. And I'm confused.
I've found a tutorial about making a duotone image with canvas, and I'm so new to this that I can't figure out what I'm doing wrong. Maybe someone can help.
Here is my code. It displays the original image (demo_small.png), but doesn't show any of the effects on it. I guess that maybe I've to overwrite it after the last "return pixels", but I've not idea of what I'm doing so..
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
<script src="https://www.mattkandler.com/assets/application-9cbca3f8879431193adab436bd8e0cf7629ecf3752685f49f997ed4469f42826.js" type="text/javascript"></script>
</head>
<body>
<canvas id="idOfCanvasToDrawImageOn" width="img.width" height="img.height"></canvas>
<script>
//Getting the image pixels
var canvasId = 'idOfCanvasToDrawImageOn';
var imageUrl = 'demo_small.png';
var canvas = document.getElementById(canvasId);
var context = canvas.getContext('2d');
var img = new Image();
// img.crossOrigin = 'Anonymous';
img.onload = function() {
// Perform image scaling if desired size is given
var scale = 1;
context.canvas.width = img.width;
context.canvas.height = img.height;
context.scale(scale, scale);
// Draw image on canvas
context.drawImage(img, 0, 0);
// Perform filtering here
};
img.src = imageUrl;
//Then we'll need to grab the pixels from this newly created canvas image using the following function
Filters.getPixels = function(img) {
var c = this.getCanvas(img.width, img.height);
var ctx = c.getContext('2d');
ctx.drawImage(img, 0, 0);
return ctx.getImageData(0, 0, c.width, c.height);
};
//Converting to grayscale
Filters.grayscale = function(pixels) {
var d = pixels.data;
var max = 0;
var min = 255;
for (var i = 0; i < d.length; i += 4) {
// Fetch maximum and minimum pixel values
if (d[i] > max) {
max = d[i];
}
if (d[i] < min) {
min = d[i];
}
// Grayscale by averaging RGB values
var r = d[i];
var g = d[i + 1];
var b = d[i + 2];
var v = 0.3333 * r + 0.3333 * g + 0.3333 * b;
d[i] = d[i + 1] = d[i + 2] = v;
}
for (var i = 0; i < d.length; i += 4) {
// Normalize each pixel to scale 0-255
var v = (d[i] - min) * 255 / (max - min);
d[i] = d[i + 1] = d[i + 2] = v;
}
return pixels;
};
//Building a color gradient
Filters.gradientMap = function(tone1, tone2) {
var rgb1 = hexToRgb(tone1);
var rgb2 = hexToRgb(tone2);
var gradient = [];
for (var i = 0; i < (256 * 4); i += 4) {
gradient[i] = ((256 - (i / 4)) * rgb1.r + (i / 4) * rgb2.r) / 256;
gradient[i + 1] = ((256 - (i / 4)) * rgb1.g + (i / 4) * rgb2.g) / 256;
gradient[i + 2] = ((256 - (i / 4)) * rgb1.b + (i / 4) * rgb2.b) / 256;
gradient[i + 3] = 255;
}
return gradient;
};
//Applying the gradient
Filters.duotone = function(img, tone1, tone2) {
var pixels = this.getPixels(img);
pixels = Filters.grayscale(pixels);
var gradient = this.gradientMap(tone1, tone2);
var d = pixels.data;
for (var i = 0; i < d.length; i += 4) {
d[i] = gradient[d[i] * 4];
d[i + 1] = gradient[d[i + 1] * 4 + 1];
d[i + 2] = gradient[d[i + 2] * 4 + 2];
}
return pixels;
};
</script>
</body>
</html>

Ripples on image

How to achieve a similar effect on an image instead of raw canvas?
/**
* Water ripple effect.
* Original code (Java) by Neil Wallis
* #link http://www.neilwallis.com/java/water.html
*
* #author Sergey Chikuyonok (serge.che#gmail.com)
* #link http://chikuyonok.ru
*/
(function(){
var canvas = document.getElementById('c'),
/** #type {CanvasRenderingContext2D} */
ctx = canvas.getContext('2d'),
width = 400,
height = 400,
half_width = width >> 1,
half_height = height >> 1,
size = width * (height + 2) * 2,
delay = 30,
oldind = width,
newind = width * (height + 3),
riprad = 3,
mapind,
ripplemap = [],
last_map = [],
ripple,
texture,
line_width = 20,
step = line_width * 2,
count = height / line_width;
canvas.width = width;
canvas.height = height;
/*
* Water ripple demo can work with any bitmap image
* (see example here: http://media.chikuyonok.ru/ripple/)
* But I need to draw simple artwork to bypass 1k limitation
*/
with (ctx) {
fillStyle = '#a2ddf8';
fillRect(0, 0, width, height);
fillStyle = '#07b';
save();
rotate(-0.785);
for (var i = 0; i < count; i++) {
fillRect(-width, i * step, width * 3, line_width);
}
restore();
}
texture = ctx.getImageData(0, 0, width, height);
ripple = ctx.getImageData(0, 0, width, height);
for (var i = 0; i < size; i++) {
last_map[i] = ripplemap[i] = 0;
}
/**
* Main loop
*/
function run() {
newframe();
ctx.putImageData(ripple, 0, 0);
}
/**
* Disturb water at specified point
*/
function disturb(dx, dy) {
dx <<= 0;
dy <<= 0;
for (var j = dy - riprad; j < dy + riprad; j++) {
for (var k = dx - riprad; k < dx + riprad; k++) {
ripplemap[oldind + (j * width) + k] += 512;
}
}
}
/**
* Generates new ripples
*/
function newframe() {
var i, a, b, data, cur_pixel, new_pixel, old_data;
i = oldind;
oldind = newind;
newind = i;
i = 0;
mapind = oldind;
// create local copies of variables to decrease
// scope lookup time in Firefox
var _width = width,
_height = height,
_ripplemap = ripplemap,
_mapind = mapind,
_newind = newind,
_last_map = last_map,
_rd = ripple.data,
_td = texture.data,
_half_width = half_width,
_half_height = half_height;
for (var y = 0; y < _height; y++) {
for (var x = 0; x < _width; x++) {
data = (
_ripplemap[_mapind - _width] +
_ripplemap[_mapind + _width] +
_ripplemap[_mapind - 1] +
_ripplemap[_mapind + 1]) >> 1;
data -= _ripplemap[_newind + i];
data -= data >> 5;
_ripplemap[_newind + i] = data;
//where data=0 then still, where data>0 then wave
data = 1024 - data;
old_data = _last_map[i];
_last_map[i] = data;
if (old_data != data) {
//offsets
a = (((x - _half_width) * data / 1024) << 0) + _half_width;
b = (((y - _half_height) * data / 1024) << 0) + _half_height;
//bounds check
if (a >= _width) a = _width - 1;
if (a < 0) a = 0;
if (b >= _height) b = _height - 1;
if (b < 0) b = 0;
new_pixel = (a + (b * _width)) * 4;
cur_pixel = i * 4;
_rd[cur_pixel] = _td[new_pixel];
_rd[cur_pixel + 1] = _td[new_pixel + 1];
_rd[cur_pixel + 2] = _td[new_pixel + 2];
}
++_mapind;
++i;
}
}
mapind = _mapind;
}
canvas.onmousemove = function(/* Event */ evt) {
disturb(evt.offsetX || evt.layerX, evt.offsetY || evt.layerY);
};
setInterval(run, delay);
// generate random ripples
var rnd = Math.random;
setInterval(function() {
disturb(rnd() * width, rnd() * height);
}, 700);
})();
<canvas id="c"></canvas>
Just look at this majestic unicorn: codepen. Data URI used to avoid CORS problems, it'll work with any number of images (all images by default).
JS:
window.addEventListener('load',()=>{
document.querySelectorAll('img').forEach((img)=>{
var cont = document.createElement('div');
cont.style.position = 'relative'
cont.style.display = 'inline-block'
img.parentNode.insertBefore(cont,img);
img.style.verticalAlign = 'top';
cont.appendChild(img)
console.dir(img)
var c = document.createElement('canvas');
c.width = img.clientWidth
c.height = img.clientHeight
c.style.position = 'absolute'
c.style.top = '0px'
c.style.left = '0px'
cont.appendChild(c)
console.log(c)
makeRipple(c,img)
})
})
function makeRipple(el,img){
var canvas = el,
/** #type {CanvasRenderingContext2D} */
ctx = canvas.getContext('2d'),
width = img.clientWidth,
height = img.clientHeight,
half_width = width >> 1,
half_height = height >> 1,
size = width * (height + 2) * 2,
delay = 30,
oldind = width,
newind = width * (height + 3),
riprad = 3,
mapind,
ripplemap = [],
last_map = [],
ripple,
texture,
line_width = 20,
step = line_width * 2,
count = height / line_width;
canvas.width = width;
canvas.height = height;
/*
* Water ripple demo can work with any bitmap image
* (see example here: http://media.chikuyonok.ru/ripple/)
* But I need to draw simple artwork to bypass 1k limitation
*/
ctx.drawImage(img,0,0,img.clientWidth,img.clientHeight)
texture = ctx.getImageData(0, 0, width, height);
ripple = ctx.getImageData(0, 0, width, height);
for (var i = 0; i < size; i++) {
last_map[i] = ripplemap[i] = 0;
}
/**
* Main loop
*/
function run() {
console.log('bbb')
newframe();
ctx.putImageData(ripple, 0, 0);
}
/**
* Disturb water at specified point
*/
function disturb(dx, dy) {
dx <<= 0;
dy <<= 0;
for (var j = dy - riprad; j < dy + riprad; j++) {
for (var k = dx - riprad; k < dx + riprad; k++) {
ripplemap[oldind + (j * width) + k] += 512;
}
}
}
/**
* Generates new ripples
*/
function newframe() {
var i, a, b, data, cur_pixel, new_pixel, old_data;
i = oldind;
oldind = newind;
newind = i;
i = 0;
mapind = oldind;
// create local copies of variables to decrease
// scope lookup time in Firefox
var _width = width,
_height = height,
_ripplemap = ripplemap,
_mapind = mapind,
_newind = newind,
_last_map = last_map,
_rd = ripple.data,
_td = texture.data,
_half_width = half_width,
_half_height = half_height;
for (var y = 0; y < _height; y++) {
for (var x = 0; x < _width; x++) {
data = (
_ripplemap[_mapind - _width] +
_ripplemap[_mapind + _width] +
_ripplemap[_mapind - 1] +
_ripplemap[_mapind + 1]) >> 1;
data -= _ripplemap[_newind + i];
data -= data >> 5;
_ripplemap[_newind + i] = data;
//where data=0 then still, where data>0 then wave
data = 1024 - data;
old_data = _last_map[i];
_last_map[i] = data;
if (old_data != data) {
//offsets
a = (((x - _half_width) * data / 1024) << 0) + _half_width;
b = (((y - _half_height) * data / 1024) << 0) + _half_height;
//bounds check
if (a >= _width) a = _width - 1;
if (a < 0) a = 0;
if (b >= _height) b = _height - 1;
if (b < 0) b = 0;
new_pixel = (a + (b * _width)) * 4;
cur_pixel = i * 4;
_rd[cur_pixel] = _td[new_pixel];
_rd[cur_pixel + 1] = _td[new_pixel + 1];
_rd[cur_pixel + 2] = _td[new_pixel + 2];
}
++_mapind;
++i;
}
}
mapind = _mapind;
}
canvas.onmousemove = function(/* Event */ evt) {
console.log('XXXX',evt.offsetX)
disturb(evt.offsetX || evt.layerX, evt.offsetY || evt.layerY);
};
setInterval(run, delay);
// generate random ripples
var rnd = Math.random;
setInterval(function() {
console.log('aaa')
disturb(rnd() * width, rnd() * height);
}, 700);
};

Nokia Here Maps calculating the centroid of a polygon (e.g triangle) generates wrong results

As the topic says i have a polygon and want to calculate the center of mass (centroid). I take the geo-coordinates, transform them into pixel cooridinates use the formula found on http://en.wikipedia.org/wiki/Centroid and transform the the calculated pixels back into geo-coordinates.
The result seems just wrong (i can't post pictures). The relevant code snippet is:
this.drawPolygonCenter = function (mapService, coords) {
var sumY = 0;
var sumX = 0;
var partialSum = 0;
var sum = 0;
var cm = mapService.getCurrentMapReference();
var points = [];
coords.forEach(function (c, idx) {
points.push(cm.geoToPixel(c));
console.log("x: " + points[idx].x + " y: " + points[idx].y);
});
var n = points.length;
for (var i = 0; i < n - 1; i++) {
partialSum = points[i].x * points[i + 1].y - points[i + 1].x * points[i].y;
sum += partialSum;
sumX += (points[i].x + points[i + 1].x) * partialSum;
sumY += (points[i].y + points[i + 1].y) * partialSum;
}
var area = 0.5 * sum;
var div = 6 * area;
var x1 = sumX / div;
var y1 = sumY / div;
console.log("Centroid: x= " + x1 + " y= " + y1); // debug
var pinLocation = cm.pixelToGeo(Math.ceil(x1), Math.ceil(y1));
var pin = this.createCenterPin(pinLocation);
cm.objects.add(new nokia.maps.map.StandardMarker(pinLocation)); // debug
I reckon your calculation has a rounding error is due switching between pixels and lat/longs - there is no need to do this - you can work with lat/longs directly.
You can add a getCentroid() method to the Polygon class as shown:
nokia.maps.map.Polygon.prototype.getCentroid = function (arg) {
var signedArea = 0,
len = this.path.getLength(),
centroidLongitude = 0,
centroidLatitude = 0;
for (i=0; i < len; i++){
var a = this.path.get(i),
b = this.path.get( i + 1 < len ? i + 1 : 0);
signedArea +=
((a.longitude * b.latitude) - (b.longitude * a.latitude));
centroidLongitude += (a.longitude + b.longitude) *
((a.longitude * b.latitude) - (b.longitude * a.latitude));
centroidLatitude += (a.latitude + b.latitude) *
((a.longitude * b.latitude) - (b.longitude * a.latitude));
}
signedArea = signedArea /2;
centroidLongitude = centroidLongitude/ (6 * signedArea);
centroidLatitude = centroidLatitude/ (6 * signedArea);
return new nokia.maps.geo.Coordinate(centroidLatitude, centroidLongitude);
};
You can call polygon.getCentroid() (e.g. to add a marker) as follows:
map.objects.add(new nokia.maps.map.Marker(polygon.getCentroid()));
Note, you may still get some edge effects of your Polygon crosses the 180th meridian.(use the isIDL()method to check) . In this case you may need to add 360 to each latitude prior to making the calculations, and substract it from the final result.

3-D semi-circle with canvas

I have a library of shapes with indicies and vertices that are functions and want to create a 3-D semi-sphere. But I am Having trouble with this. See below of sample 3-D sphere function. How can I create a 3-D semi-sphere function the same way? Please help!
sphere: function (spherical) {
var radius = 0.5,
theta = Math.PI,
phi = 2 * Math.PI,
latLines = 30,
longLines = 30,
currentLat,
currentLong,
currentVertex,
currentIndex,
vertices = [],
indices = [],
finalResult = {},
i,
j,
t,
m;
for (i = 0; i < (latLines + 1); i += 1) {
currentLat = i * theta / latLines;
for (j = 0; j < (longLines + 1); j += 1) {
currentVertex = latLines * i + j;
vertices[currentVertex] = [];
currentLong = j * phi / longLines;
vertices[currentVertex][0] = radius * Math.sin(currentLat) * Math.cos(currentLong);
vertices[currentVertex][1] = radius * Math.cos(currentLat);
vertices[currentVertex][2] = radius * Math.sin(currentLat) * Math.sin(currentLong);
if (spherical) {
vertices[currentVertex][0] *= (currentLat * spherical);
vertices[currentVertex][2] *= (currentLat * spherical);
}
}
}
for (t = 0; t < (latLines + 1); t += 1) {
for (m = 0; m < (longLines + 1); m += 1) {
currentIndex = 2 * ((latLines + 1) * t + m);
indices[2 * ((latLines + 1) * t + m)] = [];
indices[2 * ((latLines + 1) * t + m) + 1] = [];
indices[currentIndex][0] = longLines * t + m;
indices[currentIndex][1] = longLines * t + m + 1;
indices[currentIndex][2] = longLines * (t + 1) + m;
currentIndex += 1;
indices[currentIndex][0] = longLines * (t + 1) + m;
indices[currentIndex][1] = longLines * (t + 1) + m + 1;
indices[currentIndex][2] = longLines * t + m + 1;
}
}
finalResult.vertices = vertices;
finalResult.indices = indices;
return finalResult;
},

Categories