I have the following mesh which is generated by random points and creating triangles using Delaunay triangulation. Then I apply spring force per triangle on each of its vertices. But for some reason the equilibrium is always shifted to the left.
Here is a video of the behaviour:
https://youtu.be/gb5aj05zkIc
Why this is happening?
Here is the code for the physics:
for ( let i=0; i < mesh.geometry.faces.length; i++) {
let face = mesh.geometry.faces[i];
let a = mesh.geometry.vertices[face.a];
let b = mesh.geometry.vertices[face.b];
let c = mesh.geometry.vertices[face.c];
let p1 = Vertcies[face.a];
let p2 = Vertcies[face.b];
let p3 = Vertcies[face.c];
update_force_points(p1, p2, a, b);
update_force_points(p1, p3, a, c);
update_force_points(p2, p3, b, c);
}
function update_force_points(p1, p2, p1p, p2p) {
// get all the verticies
var dx = (p1.x - p2.x);
var dy = (p1.y - p2.y);
var len = Math.sqrt(dx*dx + dy*dy);
let fx = (ks * (len - r) * (dx/len)) + ((kd * p2.vx - p1.vx));
let fy = (ks * (len - r) * (dy/len)) + ((kd * p2.vy - p1.vy));
if ( ! p1.fixed ) {
p1.fx = (ks * (len - r) * (dx/len)) + ((kd * p2.vx - p1.vx));
p1.fy = (ks * (len - r) * (dy/len)) + ((kd * p2.vy - p1.vy));
}
if ( ! p2.fixed ) {
p2.fx = -1 * p1.fx;
p2.fy = -1 * p1.fy;
}
p1.vx += p1.fx / mass;
p1.vy += p1.fy / mass;
p2.vx += p2.fx / mass;
p2.vy += p2.fy / mass;
p1.x += p1.vx;
p1.y += p1.vy;
p2.x += p2.vx;
p2.y += p2.vy;
p1p.x = p1.x;
p1p.y = p1.y;
p2p.x = p2.x;
p2p.y = p2.y;
p2p.z = 0.0;
p1p.z = 0.0;
}
At the moment you're doing velocity calculations and assigning new positions at the same time, so the balance will change depending on the order that you cycle through points in. I would guess that points at the bottom left are either at the beginning of the vertex list, or at the end.
try doing all the p#.vx calculations linearly, then do a second pass where you just do p#.x += p#.vx
that way you calculate all necessary velocities based on a snapshot of where points were the previous frame, then you update their positions after all points have new velocities.
So do:
for(var i = 0; i < #; i++){
updateforces(bla,bla,bla) //don't assign position in here, just add forces to the velocity
}
for(var i =0; i < #; i++){
updateposition(bla,bla,bla)
}
Related
I am trying to make my own 3D renderer in JavaScript using raycasting, but despite checking over the math and the code countless times, it still does not seem to be working. I've tried everything I possibly could to get this thing to work and it won't, so I'm hoping someone else can figure it out.
My code runs an Update method every frame, increasing the yaw (Camera.Rot.Yaw) by 0.1 radians every iteration, but it ends up looking weird and unrealistic, and I can't figure out why. Sorry if it's confusing and long, I can't really think of a way to make a minimal reproducible example of this.
This is the Update method:
Update(Canvas, Ctx, Map, Camera) {
var id = Ctx.getImageData(0, 0, Canvas.width, Canvas.height);
var Pixels = id.data;
//Distance of projection plane from camera
//It should be behind I think
var PlaneDist = 64;
//Divides the second slopes by this so each ray goes a shorter
//distance each iteration, effectively increasing quality
var Quality = 160;
//The midpoint of the projection plane for each coordinate
var MidX =
Camera.Pos.X +
PlaneDist * Math.cos(Camera.Rot.Pitch) * Math.cos(Camera.Rot.Yaw);
var MidY = Camera.Pos.Y + PlaneDist * Math.sin(Camera.Rot.Pitch);
var MidZ =
Camera.Pos.Z +
PlaneDist * Math.cos(Camera.Rot.Pitch) * Math.sin(Camera.Rot.Yaw);
//Slopes to get to other points on the projection plane
var SlopeX =
Math.sin(Camera.Rot.Yaw) +
(Canvas.height / Canvas.width) *
Math.cos(Camera.Rot.Yaw) *
Math.sin(Camera.Rot.Pitch);
var SlopeY = -Math.cos(Camera.Rot.Pitch);
var SlopeZ =
Math.cos(Camera.Rot.Yaw) +
(Canvas.height / Canvas.width) *
Math.sin(Camera.Rot.Yaw) *
Math.sin(Camera.Rot.Pitch);
//Loops for every point on the projection plane
for (let i = 0; i < Canvas.height; i++) {
for (let j = 0; j < Canvas.width; j++) {
let NewX = Camera.Pos.X;
let NewY = Camera.Pos.Y;
let NewZ = Camera.Pos.Z;
//Slopes for the actual ray to follow, just the distance between
//the plane point and the camera divided by quality
let SlopeX2 = (Camera.Pos.X-(MidX - SlopeX * (j - Canvas.width / 2)))/ Quality;
let SlopeY2 = (Camera.Pos.Y-(MidY - SlopeY * (i - Canvas.height / 2))) / Quality;
let SlopeZ2 = (Camera.Pos.Z-(MidZ - SlopeZ * (j - Canvas.width / 2)))/ Quality;
//Ray's current map position, divides the map into a 16x32x16
//list of blocks (map initialization shown elsewhere)
let MapPos =
Map.MData[0][Math.floor(NewX / 16) + 2][Math.floor(NewY / 16)][
Math.floor(NewZ / 16)
];
//Iterates until ray either hits a block with max opacity, or
//hits the boundary of the map
while (
MapPos[3] !== 255 &&
NewX + SlopeX2 < 256 &&
NewY + SlopeY2 < 512 &&
NewZ + SlopeZ2 < 256 &&
NewX + SlopeX2 >= 0 &&
NewY + SlopeY2 >= 0 &&
NewZ + SlopeZ2 >= 0
) {
//Advances ray's current position according to slopes
NewX += SlopeX2;
NewY += SlopeY2;
NewZ += SlopeZ2;
MapPos =
Map.MData[0][Math.floor(NewX / 16) + 2][Math.floor(NewY / 16)][
Math.floor(NewZ / 16)
];
}
//Sets pixel on screen to the color of the block the ray hit
//or just white (opacity 0) if it hit the boundary
Pixels[(i * id.width + j) * 4] = MapPos[0];
Pixels[(i * id.width + j) * 4 + 1] = MapPos[1];
Pixels[(i * id.width + j) * 4 + 2] = MapPos[2];
Pixels[(i * id.width + j) * 4 + 3] = MapPos[3];
}
}
//Displays the final image
Ctx.putImageData(id, 0, 0);
}
The map initialization (CreateChunk) looks like this:
constructor() {
this.MData = [];
}
CreateChunk(X, Y) {
let Chunk = [X, Y];
for (let x = 0; x < 16; x++) {
let Plane = [];
for (let y = 0; y < 32; y++) {
let Row = [];
for (let z = 0; z < 16; z++) {
//Colors are just to help tell which pixels are at what coordinates
if (y < 8) Row.push([x * 15, y * 7, z * 15, 255]);
else Row.push([0, 0, 0, 0]);
}
Plane.push(Row);
}
Chunk.push(Plane);
}
this.MData.push(Chunk);
}
I'm hoping it's just some coding mistake I've made, but despite my countless checks it may be the trigonometry that's wrong.
I'm trying to construct a Voronoi diagram using p5.js. So far I have managed to create an image representation of it by coloring pixels that belong to the same region, using the algorithm from this video. Here how it looks like:
Here's the code (a bit messy and terribly inefficient)
const h = 512
const w = 512
const scl = 512;
const rez = h / scl;
const tiles = []
let points = []
function randomIntFromInterval(min, max) { // min and max included
return Math.floor(Math.random() * (max - min + 1) + min)
}
function setup() {
createCanvas(w, h);
background(220);
for (let i = 0; i < 12; i++) {
const p = randomIntFromInterval(0, h * h)
points.push(p)
}
for (let y = 0; y < scl; y++) {
for (let x = 0; x < scl; x++) {
tiles.push(0);
const xx = x * rez;
const yy = y * rez;
noFill();
stroke(0);
rect(xx, yy, rez, rez);
const indx = `${x + y * scl}`
if (+indx === points[0] || +indx === points[1] || +indx === points[2]) {
stroke(225, 0, 0)
fill(255, 0, 0)
}
text(indx, xx + rez / 2 - 5, yy + rez / 2 + 5)
}
}
compute(0, scl, scl)
for (const p of points) {
const x = p % scl;
const y = (p - x) / scl;
fill(0)
circle(x * rez, y * rez, 5)
}
}
function compute(x, len, grandLen) {
const corners = getCorners(x, len, grandLen)
const lookup = []
for (const corner of corners) {
const ds = []
for (const point of points) {
ds.push(distance(corner, point, scl));
}
lookup.push(ds)
}
const is = []
for (let i = 0; i < lookup.length; i++) {
const min = Math.min(...lookup[i])
const iMin = lookup[i].indexOf(min);
is.push(iMin);
}
if (is.every((val, i, arr) => val === arr[0])) {
const colorR = map(points[is[0]], 0, h*h, 0, 255)
const colorG = 255 - map(points[is[0]], 0, h*h, 0, 255)
paintRegion(corners[0], len, rez, color(colorR, colorG, 200))
} else {
const rects = divide(corners[0], len)
rects.forEach(r => {
compute(r, len / 2, grandLen)
})
}
}
function paintRegion(a, len, size, color) {
let ax;
let ay;
[ax, ay] = toCoords(a);
fill(color)
noStroke(0);
rect(ax * size, ay * size, size * len, size * len)
}
function toCoords(index) {
const x = index % scl;
const y = Math.floor((index - x) / scl);
return [x, y]
}
function distance(a, b, len) {
let ax;
let ay;
let bx;
let by;
[ax, ay] = toCoords(a);
[bx, by] = toCoords(b);
const p1 = Math.pow(bx - ax, 2);
const p2 = Math.pow(by - ay, 2);
return sqrt(p1 + p2);
}
// l1 is square, l2 is canvas
function getCorners(a, l1, l2) {
const corners = []
corners.push(a);
corners.push(a + l1 - 1);
corners.push(a + (l1 - 1) * l2)
corners.push(a + (l1 - 1) * l2 + (l1 - 1));
return corners
}
function divide(a, len) {
let ax;
let ay;
[ax, ay] = toCoords(a);
const d = len / 2;
const p1 = ax + ay * scl;
const p2 = ax + d + ay * scl;
const p3 = ax + (ay + d) * scl;
const p4 = ax + d + (ay + d) * scl;
return [p1, p2, p3, p4];
}
function draw() {
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.5.0/lib/p5.js"></script>
My problem is, such representation isn't very useful for me. I need get coordinates of regions' edges (including edges of a canvas). I've searched for ways to find edges of a shape on a 2d plane, but I feel like this is a very backwards approach. Is there a way to calculate a Voronoi diagram edges? If not, what's the simplest way to find edges by going over the pixels array?
I know there is quite a few resources on how to do this using numpy or in matlab, but I actually need a javaScript solution, if possible.
UPD: As I was researching the topic further and looking into related question brought up by Cristian in the comments, I came up to a conclusion that Future's algorithms is the best option for getting regions' edges. Fortunately, Wikipedia has links to its implementations. I tried this great implementation by Raymond Hill and it worked out great.
I was making a agar.io like game. i have a separate function which separates the blobs or circle from overlapping. it does prevent them from overlapping but changes the speed of the blobs. i dont want any change in the rate of movement. bigger or more massive blobs have slower speed than the smaller ones. but my separate function cause the bigger blobs to move with smaller blobs. here's the code i use for separating the blobs.
function separatePlayer() {
blobsPlayer.forEach(function(a, i) {
blobsPlayer.forEach(function(b, j) {
if (i == j)
return;
var ref1 = a;
var ref2 = b;
/*if(a.mass > b.mass) {
ref1 = a;
ref2 = b;
} else {
ref1 = b;
ref2 = a;
}*/
var x = ref1.x - ref2.x;
var y = ref1.y - ref2.y;
var d = Math.hypot(x, y);
var r = toRadius(a.mass) + toRadius(b.mass);
var moveCheck = !(Math.abs(a.move.x) > 2 || Math.abs(a.move.y) > 2 || Math.abs(b.move.x) > 2 || Math.abs(b.move.y) > 2);
if (d < r && moveCheck && (!canCombine(a) || !canCombine(b))) {
x /= d;
y /= d;
ref2.x += (ref1.x - x * r - ref2.x) * 0.2;
ref2.y += (ref1.y - y * r - ref2.y) * 0.2;
}
});
});
};
i tried taking massive blobs as frame-of-reference and it did work. but i dont think thats how the original game works. anyways i just dont want to change the speeds of the blobs.
EDIT: Move function and speed
function getSpeed(n) {
return 16 * 1.6 / Math.pow(toRadius(n), 0.32);
};
function mouseMovePlayer() {
blobsPlayer.forEach(function(blob) {
var angle = getMouseAngle(blob.x, blob.y);
var speed = getSpeed(blob.mass);
var x = mouseX - width / 2 + (cameraX - blob.x) * zoom;
var y = mouseY - height / 2 + (cameraY - blob.y) * zoom;
// blob.x += Math.cos(angle) * speed * Math.min(1, Math.pow(x/toRadius(blob.mass), 2));
// blob.y += Math.sin(angle) * speed * Math.min(1, Math.pow(y/toRadius(blob.mass), 2));
blob.x += Math.cos(angle) * speed;
blob.y += Math.sin(angle) * speed;
});
};
full codepen: https://codepen.io/anon/pen/ZxeaYQ
My question probably has an easy answer but I can't seem to find a solution for my problem.
I have some code to do a put request to the phillips hue api and it requires:
{"xy":[0.300,0.300]} but JSON.stringify() returns {"xy":["0.300","0.300"]}
how can I get it to return the correct values?
Here is my code:
function AllRed() {
var url = 'http://192.168.168.124/api/husm18zj4JGeeHxoUKwDrCVfKRw6nicB25dLnYHX/groups/*/action';
var r = 255;
var g = 0;
var b = 0;
var model = 'LCT001';
var data = JSON.stringify({'xy': RGBtoXY(r, g, b, model)});//RGBtoXY(r, g, b, model)
$.put(url, data);
}
And here is the code for RGBtoXY which I got from another thread here on stackoverflow, unfortunately I can't find the specific thread anymore.
function XYPoint(x, y)
{
if (this instanceof XYPoint)
{
this.x = x;
this.y = y;
} else
{
return new XYPoint(x, y);
}
}
/**
* Get Color points according to light model
* #param model string Ex: LLC010
* #returns {Array}
*/
function colorPointsForModel(model)
{
var colorPoints = [];
if (model === 'LCT001')
{
colorPoints.push(XYPoint(0.500, 0.322));
colorPoints.push(XYPoint(0.4091, 0.518));
colorPoints.push(XYPoint(0.167, 0.04));
} else if (model === 'LLC006' || model === 'LLC007')
{
colorPoints.push(XYPoint(0.704, 0.296));
colorPoints.push(XYPoint(0.2151, 0.7106));
colorPoints.push(XYPoint(0.138, 0.08));
} else
{
// Default construct triangle wich contains all values
colorPoints.push(XYPoint(1.0, 0.0));
colorPoints.push(XYPoint(0.0, 1.0));
colorPoints.push(XYPoint(0.0, 0.0));
}
return colorPoints;
}
/**
* Method to see if the given XY value is within the reach of the lamps.
*
* #param p the point containing the X,Y value
* #param colorPoints color points array containing RGB XYPoints
* #return true if within reach, false otherwise.
*/
function checkPointInLampsReach(p, colorPoints)
{
var red = colorPoints[0];
var green = colorPoints[1];
var blue = colorPoints[2];
var v1 = XYPoint(green.x - red.x, green.y - red.y);
var v2 = XYPoint(blue.x - red.x, blue.y - red.y);
var q = XYPoint(p.x - red.x, p.y - red.y);
var s = crossProduct(q, v2) / crossProduct(v1, v2);
var t = crossProduct(v1, q) / crossProduct(v1, v2);
return ((s >= 0.0) && (t >= 0.0) && (s + t <= 1.0));
}
/**
* Is Not a number?
* Note: NaN is the only JavaScript value that is treated as unequal to itself
* #param val
* #returns {boolean}
*/
function isNaN(val)
{
return val !== val;
}
/**
* Calculates crossProduct of two 2D vectors / points.
*
* #param p1 first point used as vector
* #param p2 second point used as vector
* #return crossProduct of vectors
*/
function crossProduct(p1, p2)
{
return (p1.x * p2.y - p1.y * p2.x);
}
/**
* Converts RGB to XY and Brightness
* #param r integer 0-255
* #param g integer 0-255
* #param b integer 0-255
* #param model string
*/
function RGBtoXY(red, green, blue, model)
{
if (red > 1 || green > 1 || blue > 1)
{
red /= 255;
green /= 255;
blue /= 255;
}
red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92);
green = (green > 0.04045) ? Math.pow((green + 0.055) / (1.0 + 0.055), 2.4) : (green / 12.92);
blue = (blue > 0.04045) ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : (blue / 12.92);
var X = red * 0.649926 + green * 0.103455 + blue * 0.197109;
var Y = red * 0.234327 + green * 0.743075 + blue * 0.022598;
var Z = red * 0.0000000 + green * 0.053077 + blue * 1.035763;
var cx = X / (X + Y + Z);
var cy = Y / (X + Y + Z);
if (isNaN(cx)) {
cx = 0.0;
}
if (isNaN(cy)) {
cy = 0.0;
}
//Check if the given XY value is within the colourreach of our lamps.
var xyPoint = XYPoint(cx, cy);
var colorPoints = colorPointsForModel(model);
var inReachOfLamps = checkPointInLampsReach(xyPoint, colorPoints);
if (!inReachOfLamps)
{
//It seems the colour is out of reach
//let's find the closest colour we can produce with our lamp and send this XY value out.
//Find the closest point on each line in the triangle.
var pAB = getClosestPointToPoints(colorPoints[cptRED], colorPoints[cptGREEN], xyPoint);
var pAC = getClosestPointToPoints(colorPoints[cptBLUE], colorPoints[cptRED], xyPoint);
var pBC = getClosestPointToPoints(colorPoints[cptGREEN], colorPoints[cptBLUE], xyPoint);
//Get the distances per point and see which point is closer to our Point.
var dAB = getDistanceBetweenTwoPoints(xyPoint, pAB);
var dAC = getDistanceBetweenTwoPoints(xyPoint, pAC);
var dBC = getDistanceBetweenTwoPoints(xyPoint, pBC);
var lowest = dAB;
var closestPoint = pAB;
if (dAC < lowest) {
lowest = dAC;
closestPoint = pAC;
}
if (dBC < lowest) {
lowest = dBC;
closestPoint = pBC;
}
//Change the xy value to a value which is within the reach of the lamp.
cx = closestPoint.x;
cy = closestPoint.y;
}
retval = [cx.toPrecision(3), cy.toPrecision(3)];
return retval;
}
/**
* Find the closest point on a line.
* This point will be within reach of the lamp.
*
* #param A the point where the line starts
* #param B the point where the line ends
* #param P the point which is close to a line.
* #return the point which is on the line.
*/
function getClosestPointToPoints(A, B, P)
{
var AP = XYPoint(P.x - A.x, P.y - A.y);
var AB = XYPoint(B.x - A.x, B.y - A.y);
var ab2 = AB.x * AB.x + AB.y * AB.y;
var ap_ab = AP.x * AB.x + AP.y * AB.y;
var t = ap_ab / ab2;
if (t < 0.0) {
t = 0.0;
} else if (t > 1.0) {
t = 1.0;
}
return XYPoint(A.x + AB.x * t, A.y + AB.y * t);
}
/**
* Find the distance between two points.
*
* #param one
* #param two
* #return the distance between point one and two
*/
// + (float)getDistanceBetweenTwoPoints:(CGPoint)one point2:(CGPoint)two {
function getDistanceBetweenTwoPoints(one, two)
{
var dx = one.x - two.x; // horizontal difference
var dy = one.y - two.y; // vertical difference
return Math.sqrt(dx * dx + dy * dy);
}
function XYtoRGB(x, y, brightness, model)
{
var xy = XYPoint(x, y);
var colorPoints = colorPointsForModel(model);
var inReachOfLamps = checkPointInLampsReach(xy, colorPoints);
console.log('inReachOfLamps', inReachOfLamps);
if (!inReachOfLamps) {
//It seems the colour is out of reach
//let's find the closest colour we can produce with our lamp and send this XY value out.
//Find the closest point on each line in the triangle.
var pAB = getClosestPointToPoints(colorPoints[cptRED], colorPoints[cptGREEN], xy);
var pAC = getClosestPointToPoints(colorPoints[cptBLUE], colorPoints[cptRED], xy);
var pBC = getClosestPointToPoints(colorPoints[cptGREEN], colorPoints[cptBLUE], xy);
//Get the distances per point and see which point is closer to our Point.
var dAB = getDistanceBetweenTwoPoints(xy, pAB);
var dAC = getDistanceBetweenTwoPoints(xy, pAC);
var dBC = getDistanceBetweenTwoPoints(xy, pBC);
var lowest = dAB;
var closestPoint = pAB;
if (dAC < lowest) {
lowest = dAC;
closestPoint = pAC;
}
if (dBC < lowest) {
lowest = dBC;
closestPoint = pBC;
}
//Change the xy value to a value which is within the reach of the lamp.
xy.x = closestPoint.x;
xy.y = closestPoint.y;
}
var x = xy.x;
var y = xy.y;
var z = 1.0 - x - y;
var Y = brightness;
var X = (Y / y) * x;
var Z = (Y / y) * z;
var r = X * 3.2410 - Y * 1.5374 - Z * 0.4986;
var g = -X * 0.9692 + Y * 1.8760 + Z * 0.0416;
var b = X * 0.0556 - Y * 0.2040 + Z * 1.0570;
r = r <= 0.0031308 ? 12.92 * r : (1.0 + 0.055) * Math.pow(r, (1.0 / 2.4)) - 0.055;
g = g <= 0.0031308 ? 12.92 * g : (1.0 + 0.055) * Math.pow(g, (1.0 / 2.4)) - 0.055;
b = b <= 0.0031308 ? 12.92 * b : (1.0 + 0.055) * Math.pow(b, (1.0 / 2.4)) - 0.055;
if (r < 0)
{
r = 0;
}
if (g < 0)
{
g = 0;
}
if (b < 0)
{
b = 0;
}
if (r > 1 || g > 1 || b > 1)
{
var max = Math.max(r, g, b);
r /= max;
g /= max;
b /= max;
}
r *= 255;
g *= 255;
b *= 255;
r = Math.round(r);
g = Math.round(g);
b = Math.round(b);
return {
r: r,
g: g,
b: b
};
}
Your "problem" is this line
retval = [cx.toPrecision(3), cy.toPrecision(3)];
As already noted in comments toPrecision() returns a string, which when serialized will result in the ".
So either drop the toPrecision() calls here, or convert to a number again later:
var xy = RGBtoXY(r, g, b, model);
// convert to numbers again
xy = xy.map( Number );
var data = JSON.stringify({'xy': xy });//RGBtoXY(r, g, b, model)
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.