JS variables and calculations failling; - javascript

Based on copy-engineering of someone else's code, I created the following code (see fiddle here) :
//INITIAL DATA:
var geometry.id = "Norway";
var bounds = [[-5, 40], [10, 50]];
// START CALCULATIONS
// WNES for West, North, East, South.
// WNES borders' geo-coordinates (decimal degrees)
var WNES = "",
WNES.item = geometry.id,
WNES.W = bounds[0][0],
WNES.N = bounds[1][1],
WNES.E = bounds[1][0],
WNES.S = bounds[0][1];
// Area's geo-dimensions (decimal degrees)
var WNES.geo_width = (WNES.E - WNES.W),
WNES.geo_height = (WNES.N - WNES.S);
// add a 5% padding on all WNES sides.
var WNESplus.W = WNES.W - WNES.geo_width * 0.05,
WNESplus.N = WNES.N + WNES.geo_height * 0.05,
WNESplus.E = WNES.E + WNES.geo_width * 0.05,
WNESplus.S = WNES.S - WNES.geo_height * 0.05,
WNESplus.geo_width = (WNESplus.E - WNESplus.W),
WNESplus.geo_height = (WNESplus.N - WNESplus.S);
// calcul center geo-coordinates
var WNES.lat_center = (WNES.S + WNES.N) / 2,
WNES.lon_center = (WNES.W + WNES.E) / 2;
//TEST:
console.log("Test" + WNESplus.N + " and " + WNESplus.geo_width);
This completely fails. It seems I do assignment, and semicolon use wrongly. What is my mistake, How to prossess properly ?

var is used to declare local variables. Using it to "declare" object properties is incorrect.
You should just be doing something like:
WNES.geo_width = (WNES.E - WNES.W);
WNES.geo_height = (WNES.N - WNES.S);
That said, you appear to be trying to assign properties to a literal string. This will not work. You should probably start with:
var WNES = {};

You are setting object properties without creating it. You can't use dot notation in variable declaration.
Try below:
//INITIAL DATA:
var bounds = [[-5, 40], [10, 50]];
var geometry = {};
geometry.id = "Norway";
// START CALCULATIONS
// WNES borders' geo-coordinates (decimal degrees for West, North, East, South borders)
var WNES = {};
WNES.item = geometry.id,
WNES.W = bounds[0][0],
WNES.N = bounds[1][1],
WNES.E = bounds[1][0],
WNES.S = bounds[0][1];
// Area's geo-dimensions (decimal degrees)
WNES.geo_width = (WNES.E - WNES.W),
WNES.geo_height = (WNES.N - WNES.S);
// add a 5% padding on all WNES sides.
var WNESplus = {};
WNESplus.W = WNES.W - WNES.geo_width * 0.05,
WNESplus.N = WNES.N + WNES.geo_height * 0.05,
WNESplus.E = WNES.E + WNES.geo_width * 0.05,
WNESplus.S = WNES.S - WNES.geo_height * 0.05,
WNESplus.geo_width = (WNESplus.E - WNESplus.W),
WNESplus.geo_height = (WNESplus.N - WNESplus.S);
// calcul center geo-coordinates
WNES.lat_center = (WNES.S + WNES.N) / 2,
WNES.lon_center = (WNES.W + WNES.E) / 2;
console.log("Test"+ WNESplus.N +" and "+ WNESplus.geo_width);
jsFiddle

You cannot assign properties of an object (geometry, WNES, WNESplus) using var. You have to first initialize them as objects and then assign properties:
//INITIAL DATA:
var bounds = [[-5, 40], [10, 50]];
var geometry = {};
geometry.id = "Norway";
// START CALCULATIONS
// WNES borders' geo-coordinates (decimal degrees for West, North, East, South borders)
var WNES = {};
WNES.item = geometry.id;
WNES.W = bounds[0][0];
WNES.N = bounds[1][1];
WNES.E = bounds[1][0];
WNES.S = bounds[0][1];
// Area's geo-dimensions (decimal degrees)
WNES.geo_width = (WNES.E - WNES.W);
WNES.geo_height = (WNES.N - WNES.S);
// add a 5% padding on all WNES sides.
var WNESplus = {};
WNESplus.W = WNES.W - WNES.geo_width * 0.05;
WNESplus.N = WNES.N + WNES.geo_height * 0.05;
WNESplus.E = WNES.E + WNES.geo_width * 0.05;
WNESplus.S = WNES.S - WNES.geo_height * 0.05;
WNESplus.geo_width = (WNESplus.E - WNESplus.W);
WNESplus.geo_height = (WNESplus.N - WNESplus.S);
// calcul center geo-coordinates
WNES.lat_center = (WNES.S + WNES.N) / 2;
WNES.lon_center = (WNES.W + WNES.E) / 2;
console.log("Test " + WNESplus.N + " and " + WNESplus.geo_width);
Here is workign jsFiddle

You are not allowed to declare a variable as an object in this format. Everything that includes dot notation or object, should be declared as var something = {}. And as an example:
This geometry.id = "Norway"; will 100% fail. But you can declare it as:
var geometry = { id: "Norway"}; and follow the same template for the others as well.

Related

Rotation in 3D Rigid-Body Simulator (inertia parameters, fluid drag and lift of control surface)

The bounty expires in 2 days. Answers to this question are eligible for a +500 reputation bounty.
handle wants to draw more attention to this question:
Locate and fix the error causing erratic movement, so the "vehicle" moves in a circle. Ideally, the main body of the vehicle causes drag and lift, so that it will begin to move straight and eventually stop if thrust and rudder angle are set to zero.
My attempt to implement/port parts of the rigid body 3D simulator (airplane) from "Physics for Game Developers, 2nd Edition" to Javascript and Three.js seems to be failing somewhere around the rotational part: while the vehicle (body and rudder) accelerates OK it does not properly move and turn:
At times (higher value for RHO) it appears to oscillate, resulting in "weird jerky" motion
The control surface lift then seems to toggle sign
While the calculations are 3D, there are currently no vertical forces (Z) like gravity/lift/bouyancy, so motion takes place on the X-Y plane.
It's likely that I've made mistakes porting the Vector/Matrix/Quaternion calculations or that the model parameters are way off (vLocalInertia vectors that determine the inertia tensor are a likely candidate) or the behavior is due to the simple Euler-type integration.
Of course it's also possible the original code (listings) is erroneous (submitted errata).
I'd like to be able to "drive" circles on the screen by setting the rudder and minimum thrust. Ideally, the vehicles body also causes drag and lift so it will tend to go straight and slow down if no external force (thrust) is applied.
body {
margin: 0px;
}
canvas {
display: block;
}
<div id="container"></div>
<!--script src="https://unpkg.com/es-module-shims#1.3.6/dist/es-module-shims.js"></script-->
<script type="importmap">
{ "imports": { "three": "https://unpkg.com/three/build/three.module.js", "three/addons/": "https://unpkg.com/three/examples/jsm/" } }
</script>
<script type="module">
import * as THREE from "three";
import { Euler, Matrix3, Vector3 } from "three";
/** degree to radian */
function rad(deg) {
return (deg / 180) * Math.PI;
}
/** radian to degree */
function deg(rad) {
return (rad / Math.PI) * 180;
}
/** */
function vstr(v) {
return `X${v.x.toFixed(3)}, Y${v.y.toFixed(3)}, Z${v.z.toFixed(3)}, `
}
/** */
class Vehicle {
constructor(scene) {
this.length = 0.3;
this.width = 0.15;
this.fMass = 0.0; // total mass
this.mInertia = new Matrix3(); // mass moment of inertia in body coordinates
this.mInertiaInverse = new Matrix3(); // inverse of mass moment of inertia
this.vPosition = new Vector3(0, 0, 0.001); // position in earth coordinates
this.vVelocity = new Vector3(); // velocity in earth coordinates
this.vVelocityBody = new Vector3(); // velocity in body coordinates
this.vAngularVelocity = new Vector3(); // angular velocity in body coordinates
this.vEulerAngles = new Vector3(0, 0, 0); // Euler angles in body coordinates
this.fSpeed = 0.0; // speed (magnitude of the velocity)
this.qOrientation = new THREE.Quaternion(); // orientation in earth coordinates
this.qOrientation.setFromEuler(new Euler().setFromVector3(this.vEulerAngles));
// console.log("vEulerAngles", this.vEulerAngles);
// console.log("qOrientation", this.qOrientation);
this.vForces = new Vector3(); // total force on body
this.vMoments = new Vector3(); // total moment (torque) on body
this.elements = [
{
type: "main",
fMass: 1.0,
vDCoords: new Vector3(0.0, 0.0, 0.0),
vCGCoords: null,
vLocalInertia: new Vector3(.1, .1, .1), // TODO:
rotates: false,
fIncidence: 0.0,
fDihedral: 90.0,
fArea: 0.5,
},
{
type: "rudder",
fMass: 0.01,
vDCoords: new Vector3(-.15, 0.0, 0.0),
vCGCoords: null,
vLocalInertia: new Vector3(.01, .01, .01), // TODO:
rotates: true,
fIncidence: 0.0,
fDihedral: 90.0,
fArea: 0.2,
},
];
// Calculate the vector normal (perpendicular) to each lifting surface.
// This is required when you are calculating the relative air velocity for lift and drag calculations.
for (let e of this.elements) {
let inc = rad(e.fIncidence);
let dih = rad(e.fDihedral);
e.vNormal = new Vector3(
Math.sin(inc), //
Math.cos(inc) * Math.sin(dih), //
Math.cos(inc) * Math.cos(dih) //
);
e.vNormal.normalize();
// console.log(e.vNormal);
}
// Calculate total mass
for (let e of this.elements) {
this.fMass += e.fMass;
}
console.log("this.fMass ", this.fMass);
// Calculate combined center of gravity location
let vMoment = new Vector3();
for (let e of this.elements) {
// vMoment += e.fMass * e.vDCoords;
vMoment.add(e.vDCoords.clone().multiplyScalar(e.fMass))
}
// CG = vMoment/mass;
let CG = vMoment.divideScalar(this.fMass);
console.log("CG", CG);
// Calculate coordinates of each element with respect to the combined CG
for (let e of this.elements) {
// e.vCGCoords = e.vDCoords - CG;
e.vCGCoords = e.vDCoords.clone().sub(CG);
console.log("e.vCGCoords", e.vCGCoords);
}
// (This inertia matrix (tensor) is in body coordinates)
let Ixx = 0;
let Iyy = 0;
let Izz = 0;
let Ixy = 0;
let Ixz = 0;
let Iyz = 0;
for (let e of this.elements) {
Ixx += e.vLocalInertia.x + e.fMass * (e.vCGCoords.y * e.vCGCoords.y + e.vCGCoords.z * e.vCGCoords.z);
Iyy += e.vLocalInertia.y + e.fMass * (e.vCGCoords.z * e.vCGCoords.z + e.vCGCoords.x * e.vCGCoords.x);
Izz += e.vLocalInertia.z + e.fMass * (e.vCGCoords.x * e.vCGCoords.x + e.vCGCoords.y * e.vCGCoords.y);
Ixy += e.fMass * (e.vCGCoords.x * e.vCGCoords.y);
Ixz += e.fMass * (e.vCGCoords.x * e.vCGCoords.z);
Iyz += e.fMass * (e.vCGCoords.y * e.vCGCoords.z);
}
// mass, inertia matrix and the inverse
// this.fMass = mass;
this.mInertia.e11 = Ixx;
this.mInertia.e12 = -Ixy;
this.mInertia.e13 = -Ixz;
this.mInertia.e21 = -Ixy;
this.mInertia.e22 = Iyy;
this.mInertia.e23 = -Iyz;
this.mInertia.e31 = -Ixz;
this.mInertia.e32 = -Iyz;
this.mInertia.e33 = Izz;
// console.log(this.mInertia);
console.log(this.mInertia.e11, this.mInertia.e12, this.mInertia.e13,);
console.log(this.mInertia.e21, this.mInertia.e22, this.mInertia.e23,);
console.log(this.mInertia.e31, this.mInertia.e32, this.mInertia.e33,);
this.mInertiaInverse = this.mInertia.clone().invert();
// angular
this.angle = 0; // [rad]
// [deg]
this.rudder = 8;
this.thrust = 0.1;
this.object = new THREE.Group();
this.object.position.copy(this.vPosition);
this.object.rotation.z = this.angle;
const axes = new THREE.AxesHelper(0.1);
this.object.add(axes);
// add self to scene
scene.add(this.object);
}
physics(dt) {
let Fb = new Vector3();
let Mb = new Vector3();
// reset forces and moments:
this.vForces.x = 0.0;
this.vForces.y = 0.0;
this.vForces.z = 0.0;
this.vMoments.x = 0.0;
this.vMoments.y = 0.0;
this.vMoments.z = 0.0;
this.elements[1].fIncidence = this.rudder;
// Define the thrust vector, which acts through the plane's CG
let ThrustForce = this.thrust;
let Thrust = new Vector3(1, 0, 0).multiplyScalar(ThrustForce);
// Calculate forces and moments in body space:
let vLocalVelocity = new Vector3();
let fLocalSpeed = 0;
let vDragVector = new Vector3();
let vLiftVector = new Vector3();
let fAttackAngle = 0;
let tmp = 0.0;
let vResultant = new Vector3();
// let i = 0;
let vtmp = new Vector3();
// Given the attack angle, this function returns the proper lift coefficient for a symmetric (no camber) airfoil without flaps.
function RudderLiftCoefficient(angle) {
let clf0 = [0.16, 0.456, 0.736, 0.968, 1.144, 1.12, 0.8];
let a = [0.0, 4.0, 8.0, 12.0, 16.0, 20.0, 24.0];
let cl = 0;
let aa = Math.abs(angle);
for (let i = 0; i < 8; i++) {
if ((a[i] <= aa) && (a[i + 1] > aa)) {
cl = clf0[i] - (a[i] - aa) * (clf0[i] - clf0[i + 1]) / (a[i] - a[i + 1]);
if (angle < 0) cl = -cl;
break;
}
}
return cl;
}
// Given the attack angle, this function returns the proper drag coefficient for a symmetric (no camber) airfoil without flaps.
function RudderDragCoefficient(angle) {
let cdf0 = [0.0032, 0.0072, 0.0104, 0.0184, 0.04, 0.096, 0.168];
let a = [0.0, 4.0, 8.0, 12.0, 16.0, 20.0, 24.0];
let cd = 0.5;
let aa = Math.abs(angle);
for (let i = 0; i < 8; i++) {
if ((a[i] <= aa) && (a[i + 1] > aa)) {
cd = cdf0[i] - (a[i] - aa) * (cdf0[i] - cdf0[i + 1]) / (a[i] - a[i + 1]);
break;
}
}
return cd;
}
// let Stalling = false;
// loop through the seven lifting elements skipping the fuselage
for (let e of this.elements) {
if (e.rotates) // The tail/rudder is a special case since it can rotate;
{
// thus, you have to recalculate the normal vector.
let inc, dih;
inc = rad(e.fIncidence); // incidence angle
dih = rad(e.fDihedral); // dihedral angle
e.vNormal = new Vector3(
Math.sin(inc), // x?
(Math.cos(inc) * Math.sin(dih)), // y?
(Math.cos(inc) * Math.cos(dih)) // z?
);
e.vNormal.normalize();
// console.log("e.vNormal", e.vNormal);
}
// Calculate local velocity at element
// The local velocity includes the velocity due to linear motion of the airplane,
// plus the velocity at each element due to the rotation of the airplane.
// Here's the rotational part
// vtmp = this.vAngularVelocity ^ e.vCGCoords;
vtmp.crossVectors(this.vAngularVelocity, e.vCGCoords);
// vLocalVelocity = this.vVelocityBody + vtmp;
vLocalVelocity.addVectors(this.vVelocityBody, vtmp);
// Calculate local air speed
fLocalSpeed = vLocalVelocity.length();
// Find the direction in which drag will act.
// Drag always acts inline with the relative velocity but in the opposing direction
const dragthreshold = 0;//.1; // 1.0 // TODO: magic number
if (fLocalSpeed > dragthreshold) {
// vDragVector = -vLocalVelocity / fLocalSpeed;
vDragVector = vLocalVelocity.clone().divideScalar(-fLocalSpeed);
//console.log("vDragVector",vDragVector);
}
// else{
// console.log("no drag");
// }
// Find the direction in which lift will act.
// Lift is always perpendicular to the drag vector
// vLiftVector = (vDragVector ^ e.vNormal) ^ vDragVector;
vLiftVector = new THREE.Vector3().crossVectors(new THREE.Vector3().crossVectors(vDragVector, e.vNormal), vDragVector);
// tmp = vLiftVector.length(); // superfluous line in book
vLiftVector.normalize();
// debugger;
// console.log("drag ", vstr(vDragVector)); // has speed threshold
// console.log("normal", vstr(e.vNormal));
// console.log("lift ", vstr(vLiftVector)); // sign toggles near zero?
// seems ok, drag -x, lift +y
// Find the angle of attack.
// The attack angle is the angle between the lift vector and the element normal vector.
// Note, the sine of the attack angle is equal to the cosine of the angle between the drag vector and the normal vector.
// tmp = vDragVector * e.vNormal;
tmp = vDragVector.dot(e.vNormal);
if (tmp > 1) tmp = 1;
if (tmp < -1) tmp = -1;
fAttackAngle = deg(Math.asin(tmp));
// console.log("fAttackAngle", fAttackAngle); // toggles sign like vLift
// Determine the resultant force (lift and drag) on the element.
// const RHO = 1.225; // kg/m^3 for AIR
const RHO = 1225; // kg/m^3
tmp = 0.5 * RHO * fLocalSpeed ** 2 * e.fArea;
if (e.type == "rudder") // Tail/rudder
{
// vResultant = (vLiftVector * RudderLiftCoefficient(fAttackAngle) + vDragVector * RudderDragCoefficient(fAttackAngle)) * tmp;
const lc = RudderLiftCoefficient(fAttackAngle);
const dc = RudderDragCoefficient(fAttackAngle);
const lift = vLiftVector.multiplyScalar(lc)
const drag = vDragVector.multiplyScalar(dc);
const liftanddrag = lift.add(drag);
vResultant = liftanddrag.multiplyScalar(tmp);
//vResultant.set(-0.0, -0.1, 0);
console.log("vResultant", vstr(vResultant));
}
// Keep a running total of these resultant forces (total force)
// Fb += vResultant;
Fb.add(vResultant);
// Calculate the moment about the CG of this element's force
// and keep a running total of these moments (total moment)
// vtmp = e.vCGCoords ^ vResultant;
vtmp.crossVectors(e.vCGCoords, vResultant);
// console.log("e.vCGCoords", vstr(e.vCGCoords));
// console.log("moment", vstr(vtmp));
// Mb += vtmp;
Mb.add(vtmp);
// debugger;
} // elements
// console.log("Mb", vstr(Mb));
// Now add the thrust
// Fb += Thrust;
Fb.add(Thrust);
// Convert forces from model space to earth space
// this.vForces = QVRotate(this.qOrientation, Fb);
this.vForces = Fb.clone().applyQuaternion(this.qOrientation);
// console.log(this.vForces);
// gravity
// const g = -9.81;
// this.vForces.z += g * this.fMass;
// moments
// this.vMoments += Mb;
this.vMoments.add(Mb);
// console.log("vMoments", vstr(this.vMoments));
// INTEGRATOR (EULER)
// calculate the acceleration of the airplane in earth space:
// Ae = this.vForces / this.fMass;
let vAe = this.vForces.clone().divideScalar(this.fMass);
// console.log(Ae);
// calculate the velocity of the airplane in earth space:
// this.vVelocity += Ae * dt;
vAe.multiplyScalar(dt);
// console.log(Ae);
this.vVelocity.add(vAe);
// console.log(this.vVelocity);
// calculate the position of the airplane in earth space:
// this.vPosition += this.vVelocity * dt;
this.vPosition.add(this.vVelocity.clone().multiplyScalar(dt));
// handle the rotations:
// calculate the angular velocity in body space:
console.log("this.vAngularVelocity", this.vAngularVelocity);
// this.vAngularVelocity += this.mInertiaInverse * (this.vMoments - (this.vAngularVelocity ^ (this.mInertia * this.vAngularVelocity))) * dt;
let mv = this.vAngularVelocity.clone().applyMatrix3(this.mInertia);
let cp = new Vector3().crossVectors(this.vAngularVelocity, mv);
let sub = this.vMoments.clone().sub(cp);
sub.multiplyScalar(dt);
this.vAngularVelocity.add(sub.applyMatrix3(this.mInertiaInverse));
console.log("this.vAngularVelocity", this.vAngularVelocity);
// calculate the new rotation quaternion:
// this.qOrientation += (this.qOrientation * this.vAngularVelocity) * (0.5 * dt);
// Quaternion * Vector
let q = this.qOrientation.clone();
let v = this.vAngularVelocity.clone();
let vq = new THREE.Quaternion(v.x, v.y, v.z, 0);
let qv = q.clone().multiply(vq);
// Q * Scalar
const scalar = 0.5 * dt;
qv.x *= scalar;
qv.y *= scalar;
qv.z *= scalar;
qv.w *= scalar;
// Q+Q
this.qOrientation.x += qv.x;
this.qOrientation.y += qv.y;
this.qOrientation.z += qv.z;
this.qOrientation.w += qv.w;
// console.log("qO",this.qOrientation);
// normalize the orientation quaternion:
// let mag = 0.0;
// mag = this.qOrientation.Magnitude();
// if (mag != 0)
// this.qOrientation /= mag;
this.qOrientation.normalize();
// console.log(this.qOrientation);
// calculate the velocity in body space:
// (we'll need this to calculate lift and drag forces)
// this.vVelocityBody = QVRotate(~this.qOrientation, this.vVelocity);
this.vVelocityBody = this.vVelocity.clone().applyQuaternion(this.qOrientation.clone().conjugate());
// calculate the air speed:
// this.fSpeed = this.vVelocity.Magnitude();
this.fSpeed = this.vVelocity.length();
// get the Euler angles for our information
// u = MakeEulerAnglesFromQ(this.qOrientation);
let u = new Vector3().setFromEuler(new Euler().setFromQuaternion(this.qOrientation));
this.vEulerAngles.x = u.x; // roll
this.vEulerAngles.y = u.y; // pitch
this.vEulerAngles.z = u.z; // yaw
// threedee
this.object.position.copy(this.vPosition);
this.object.rotation.z = this.vEulerAngles.z;
} // physics
} // Vehicle
let renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
let canvas = renderer.domElement;
document.body.appendChild(canvas);
let scene = new THREE.Scene();
scene.background = new THREE.Color("gray");
const axes = new THREE.AxesHelper();
scene.add(axes);
const grid = new THREE.GridHelper(20, 20);
grid.rotation.x = Math.PI / 2; // Z is up
scene.add(grid);
let camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 50);
camera.up.set(0, 0, 1); // Z is up
camera.position.set(-0, -0, 3);
let craft = new Vehicle(scene);
// simulation
let timeinitial = performance.now();
const timestart = timeinitial;
function step(time) {
let timedelta = (time - timeinitial) / 1000; // [s]
timeinitial = time;
// simulate
craft.physics(timedelta);
renderer.render(scene, camera);
if(time - timestart < 5*1000){
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
window.addEventListener("resize", e => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
EDIT: removed controls, added constant thrust and rudder angle.
EDIT: simplified code for vResultant - the issue seems to originate in the calculation of lift...

Converting 360 degree view to equirectangular in node js?

I have been trying to convert the 360 degree camera, single fish eye image, to equirectangular viewer in node js for the past two days. In stackoverflow, the same question is asked and answered in pseudo code. I have been trying to convert pseudo code to node js and cleared some errors. Now the project runs without error but the output image is blank.
From that pseudo, I dont know the polar_w, polar_h and geo_w, geo_h, geo and polar value, so, it gave static value to show the output. Here is a link which i followed to convert pseudo code to node js.
How to convert spherical coordinates to equirectangular projection coordinates?.
Here is the code I tried for converting spherical image to equirectangular viewer:
exports.sphereImage=(request, response)=>{
var Jimp = require('jimp');
// Photo resolution
var img_w_px = 1280;
var img_h_px = 720;
var polar_w = 1280;
var polar_h = 720;
var geo_w = 1280;
var geo_h = 720;
var img_h_deg = 70;
var img_w_deg = 30;
// Camera field-of-view angles
var img_ha_deg = 70;
var img_va_deg = 40;
// Camera rotation angles
var hcam_deg = 230;
var vcam_deg = 60;
// Camera rotation angles in radians
var hcam_rad = hcam_deg/180.0*Math.PI;
var vcam_rad = vcam_rad/180.0*Math.PI;
// Rotation around y-axis for vertical rotation of camera
var rot_y = [
[Math.cos(vcam_rad), 0, Math.sin(vcam_rad)],
[0, 1, 0],
[-Math.sin(vcam_rad), 0, Math.cos(vcam_rad)]
];
// Rotation around z-axis for horizontal rotation of camera
var rot_z = [
[Math.cos(hcam_rad), -Math.sin(hcam_rad), 0],
[Math.sin(hcam_rad), Math.cos(hcam_rad), 0],
[0, 0, 1]
];
Jimp.read('./public/images/4-18-2-42.jpg', (err, lenna) => {
polar = new Jimp(img_w_px, img_h_px);
geo = new Jimp(img_w_px, img_h_px);
for(var i=0; i<img_h_px; ++i)
{
for(var j=0; j<img_w_px; ++j)
{
// var p = img.getPixelAt(i, j);
var p = lenna.getPixelColor(i, j)
// var p = getPixels(img, { x: i, y: j })
// Calculate relative position to center in degrees
var p_theta = (j - img_w_px / 2.0) / img_w_px * img_w_deg / 180.0 * Math.PI;
var p_phi = -(i - img_h_px / 2.0) / img_h_px * img_h_deg / 180.0 *Math. PI;
// Transform into cartesian coordinates
var p_x = Math.cos(p_phi) * Math.cos(p_theta);
var p_y = Math.cos(p_phi) * Math.sin(p_theta);
var p_z = Math.sin(p_phi);
var p0 = {p_x, p_y, p_z};
// Apply rotation matrices (note, z-axis is the vertical one)
// First vertically
var p1 = rot_y[1][2][3] * p0;
var p2 = rot_z[1][2][3] * p1;
// Transform back into spherical coordinates
var theta = Math.atan2(p2[1], p2[0]);
var phi = Math.asin(p2[2]);
// Retrieve longitude,latitude
var longitude = theta / Math.PI * 180.0;
var latitude = phi / Math.PI * 180.0;
// Now we can use longitude,latitude coordinates in many different
projections, such as:
// Polar projection
{
var polar_x_px = (0.5*Math.PI + phi)*0.5 * Math.cos(theta)
/Math.PI*180.0 * polar_w;
var polar_y_px = (0.5*Math.PI + phi)*0.5 * Math.sin(theta)
/Math.PI*180.0 * polar_h;
polar.setPixelColor(p, polar_x_px, polar_y_px);
}
// Geographical (=equirectangular) projection
{
var geo_x_px = (longitude + 180) * geo_w;
var geo_y_px = (latitude + 90) * geo_h;
// geo.setPixel(geo_x_px, geo_y_px, p.getRGB());
geo.setPixelColor(p, geo_x_px, geo_y_px);
}
// ...
}
}
geo.write('./public/images/4-18-2-42-00001.jpg');
polar.write('./public/images/4-18-2-42-00002.jpg');
});
}
And tried another method by slicing image into four parts to detect car. Sliced image into four parts using image-slice module and to read and write jimp module is used. But unfortunately cars not detected properly.
Here is the code i used for slicing image:
exports.sliceImage=(request, response)=>{
var imageToSlices = require('image-to-slices');
var lineXArray = [540, 540];
var lineYArray = [960, 960];
var source = './public/images/4-18-2-42.jpg'; // width: 300, height: 300
imageToSlices(source, lineXArray, lineYArray, {
saveToDir: './public/images/',
clipperOptions: {
canvas: require('canvas')
}
}, function() {
console.log('the source image has been sliced into 9 sections!');
});
}//sliceImage
And for detect car from image i used opencv4nodejs. Cars are not detected properly. here is the code i used for detect car:
function runDetectCarExample(img=null){
if(img==null){
img = cv.imread('./public/images/section-1.jpg');
}else
{
img=cv.imread(img);
}
const minConfidence = 0.06;
const predictions = classifyImg(img).filter(res => res.confidence > minConfidence && res.className=='car');
const drawClassDetections = makeDrawClassDetections(predictions);
const getRandomColor = () => new cv.Vec(Math.random() * 255, Math.random() * 255, 255);
drawClassDetections(img, 'car', getRandomColor);
cv.imwrite('./public/images/section-'+Math.random()+'.jpg', img);
var name="distanceFromCamera";
var focalLen= 1.6 ;//Focal length in mm
var realObjHeight=254 ;//Real Height of Object in mm
var cameraFrameHeight=960;//Height of Image in pxl
var imgHeight=960;//Image Height in pxl
var sensorHeight=10;//Sensor height in mm
var R = 6378.1 //#Radius of the Earth
var brng = 1.57 //#Bearing is 90 degrees converted to radians.
var hc=(200/100);//Camera height in m
predictions
.forEach((data)=> {
// imgHeight=img.rows;//Image Height in pxl
// realObjHeight=data.rect.height;
// data.rect[name]=((focalLen)*(realObjHeight)*
(cameraFrameHeight))/((imgHeight)*(sensorHeight));
var dc=(((data.rect.width * focalLen) / img.cols)*2.54)*100; // meters
console.log(Math.floor(parseInt(data.rect.width)));
// var dc=((Math.floor(parseInt(data.rect.width)* 0.264583) * focalLen) / img.cols); // mm
var lat1=13.0002855;//13.000356;
var lon1=80.2046441;//80.204632;
// Gate 13.0002855,80.2046441
// Brazil Polsec : -19.860566, -43.969436
// var d=Math.sqrt((dc*dc)+(hc*hc));
// d=(data.rect[name])/1000;
data.rect[name]=d=dc/1000;
lat1 =toRadians(lat1);
lon1 = toRadians(lon1);
brng =toRadians(90);
// lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) +
// Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng));
// lon2 = lon1 +
Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1),
// Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));
var lat2 = Math.asin(Math.sin(lat1) * Math.cos(d/6371) +
Math.cos(lat1) * Math.sin(d/6371) * Math.cos(brng));
var lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(d/6371) * Math.cos(lat1),
Math.cos(d/6371) - Math.sin(lat1) * Math.sin(lat2));
lat2 = toDegrees(lat2);
lon2 = toDegrees(lon2);
data.rect['latLong']=lat2+','+lon2;
// console.log(brng);
});
response.send(predictions);
cv.imshowWait('img', img);
};
here is the fish eye image which need to be converted to equirectangular.
Any help much appreciated pls....
You are asking how to convert a 360deg fish-eye projection to an equirectangular projection.
In order to do this, for every pixel on the fish-eye image you need to know where to place in onto the output image.
Your input image is 1920x1080, let us assume you want to output it to an equirectangular projection of the same size.
The input circle mapping is defined as:
cx = 960; // center of circle on X-axis
cy = 540; // center of circle on Y-axis
radius = 540; // radius of circle
If you have a pixel at (x,y) in the input image, then we can calculate the spherical coordinates using:
dx = (x - cx) * 1.0 / radius;
dy = (y - cy) * 1.0 / radius;
theta_deg = atan2(dy, dx) / MATH_PI * 180;
phi_deg = acos(sqrt(dx*dx + dy*dy)) / MATH_PI * 180;
outputx = (theta_deg + 180) / 360.0 * outputwidth_px;
outputy = (phi_deg + 90) / 180.0 * outputheight_px;
So there we translated (x,y) from the fish-eye image to the (outputx,outputy) in the equirectangular image. In order to not leave the implementation as the dreaded "exercise to the reader", here is some sample Javascript-code using the Jimp-library as used by the OP:
var jimp = require('jimp');
var inputfile = 'input.png';
jimp.read(inputfile, function(err, inputimage)
{
var cx = 960;
var cy = 540;
var radius = 540;
var inputwidth = 1920;
var inputheight = 1080;
var outputwidth = 1920;
var outputheight = 1080;
new jimp(outputwidth, outputheight, 0x000000ff, function(err, outputimage)
{
for(var y=0;y<inputheight;++y)
{
for(var x=0;x<inputwidth;++x)
{
var color = inputimage.getPixelColor(x, y);
var dx = (x - cx) * 1.0 / radius;
var dy = (y - cy) * 1.0 / radius;
var theta_deg = Math.atan2(dy, dx) / Math.PI * 180;
var phi_deg = Math.acos(Math.sqrt(dx*dx + dy*dy)) / Math.PI * 180;
var outputx = Math.round((theta_deg + 180) / 360.0 * outputwidth);
var outputy = Math.round((phi_deg + 90) / 180.0 * outputheight);
outputimage.setPixelColor(color, outputx, outputy);
}
}
outputimage.write('output.png');
});
});
Note that you will still need to do blending of the pixel with neighbouring pixels (for the same reason as when you're resizing the image).
Additionally, in your case, you only have half of the sphere (you can't see the sun in the sky). So you would need to use var outputy = Math.round(phi_deg / 90.0 * outputheight). In order to keep the right aspect ratio, you might want to change the height to 540.
Also note that the given implementation may not be efficient at all, it's better to use the buffer directly.
Anyway, without blending I came up with the result as demonstrated here:
So in order to do blending, you could use the simplest method which is the nearest neighbour approach. In that case, you should invert the formulas in the above example. Instead of moving the pixels from the input image to the right place in the output image, you can go through every pixel in the output image and ask which input pixel we can use for that. This will avoid the black pixels, but may still show artifacts:
var jimp = require('jimp');
var inputfile = 'input.png';
jimp.read(inputfile, function(err, inputimage)
{
var cx = 960;
var cy = 540;
var radius = 540;
var inputwidth = 1920;
var inputheight = 1080;
var outputwidth = 1920;
var outputheight = 1080/2;
var blendmap = {};
new jimp(outputwidth, outputheight, 0x000000ff, function(err, outputimage)
{
for(var y=0;y<outputheight;++y)
{
for(var x=0;x<outputwidth;++x)
{
var theta_deg = 360 - x * 360.0 / outputwidth - 180;
var phi_deg = 90 - y * 90.0 / outputheight;
var r = Math.sin(phi_deg * Math.PI / 180)
var dx = Math.cos(theta_deg * Math.PI / 180) * r;
var dy = Math.sin(theta_deg * Math.PI / 180) * r;
var inputx = Math.round(dx * radius + cx);
var inputy = Math.round(dy * radius + cy);
outputimage.setPixelColor(inputimage.getPixelColor(inputx, inputy), x, y);
}
}
outputimage.write('output.png');
});
});
For reference, in order to convert between Cartesian and Spherical coordinate systems. These are the formulas (taken from here). Note that the z is in your case just 1, a so-called "unit" sphere, so you can just leave it out of the equations. You should also understand that since the camera is actually taking a picture in three dimensions, you also need formulas to work in three dimensions.
Here is the generated output image:
Since I don't see your original input image in your question anymore, in order for anyone to test the code from this answer, you can use the following image:
Run the code with:
mkdir /tmp/test
cd /tmp/test
npm install --permanent jimp
cat <<EOF >/tmp/test/main.js
... paste the javascript code from above ...
EOF
curl https://i.stack.imgur.com/0zWt6.png > input.png
node main.js
Note: In order to further improve the blending, you should remove the Math.round. So for instance, if you need to grab a pixel at x is 0.75, and the pixel on the left at x = 0 is white, and the pixel on the right at x = 1 is black. Then you want to mix both colors into a dark grey color (using ratio 0.75). You would have to do this for both dimensions simultaneously, if you want a nice result. But this should really be in a new question imho.

How to calculate the rotation angle of two constrained segments?

I have two vectors, the Y-aligned is fixed whereby the X-aligned is allowed to rotate. These vectors are connected together through two fixed-length segments. Given the angle between the two vectors (82.74) and the length of all segments, how can I get the angle of the two jointed segments (24.62 and 22.61)?
What is given: the magnitude of the vectors, and the angle between the X-axis and OG:
var magOG = 3,
magOE = 4,
magGH = 3,
magEH = 2,
angleGamma = 90;
This is my starting point: angleGamma = 90 - then, I will have following vectors:
var vOG = new vec2(-3,0),
vOE = new vec2(0,-4);
From here on, I am trying to get angleAlphaand angleBeta for values of angleGamma less than 90 degrees.
MAGNITUDE OF THE CONSTRAINED SEGMENTS:
Segments HG and HE must meet following conditions:
/
| OG*OG+ OE*OE = (HG + HE)*(HG + HE)
>
| OG - HG = OE - HE
\
which will lead to following two solutions (as pointed out in the accepted answer - bilateration):
Solution 1:
========================================================
HG = 0.5*(-Math.sqrt(OG*OG + OE*OE) + OG - OE)
HE = 0.5*(-Math.sqrt(OG*OG + OE*OE) - OG + OE)
Solution 2:
========================================================
HG = 0.5*(Math.sqrt(OG*OG + OE*OE) + OG - OE)
HE = 0.5*(Math.sqrt(OG*OG + OE*OE) - OG + OE)
SCRATCHPAD:
Here is a playground with the complete solution. The visualization library used here is the great JSXGraph. Thanks to the Center for Mobile Learning with Digital Technology of the Bayreuth University.
Credits for the circle intersection function: 01AutoMonkey in the accepted answer to this question: A JavaScript function that returns the x,y points of intersection between two circles?
function deg2rad(deg) {
return deg * Math.PI / 180;
}
function rad2deg(rad) {
return rad * 180 / Math.PI;
}
function lessThanEpsilon(x) {
return (Math.abs(x) < 0.00000000001);
}
function angleBetween(point1, point2) {
var x1 = point1.X(), y1 = point1.Y(), x2 = point2.X(), y2 = point2.Y();
var dy = y2 - y1, dx = x2 - x1;
var t = -Math.atan2(dx, dy); /* range (PI, -PI] */
return rad2deg(t); /* range (180, -180] */
}
function circleIntersection(circle1, circle2) {
var r1 = circle1.radius, cx1 = circle1.center.X(), cy1 = circle1.center.Y();
var r2 = circle2.radius, cx2 = circle2.center.X(), cy2 = circle2.center.Y();
var a, dx, dy, d, h, h2, rx, ry, x2, y2;
/* dx and dy are the vertical and horizontal distances between the circle centers. */
dx = cx2 - cx1;
dy = cy2 - cy1;
/* angle between circle centers */
var theta = Math.atan2(dy,dx);
/* vertical and horizontal components of the line connecting the circle centers */
var xs1 = r1*Math.cos(theta), ys1 = r1*Math.sin(theta), xs2 = r2*Math.cos(theta), ys2 = r2*Math.sin(theta);
/* intersection points of the line connecting the circle centers */
var sxA = cx1 + xs1, syA = cy1 + ys1, sxL = cx2 - xs2, syL = cy2 - ys2;
/* Determine the straight-line distance between the centers. */
d = Math.sqrt((dy*dy) + (dx*dx));
/* Check for solvability. */
if (d > (r1 + r2)) {
/* no solution. circles do not intersect. */
return [[sxA,syA], [sxL,syL]];
}
thetaA = -Math.PI - Math.atan2(cx1,cy1); /* Swap X-Y and re-orient to -Y */
xA = +r1*Math.sin(thetaA);
yA = -r1*Math.cos(thetaA);
ixA = cx1 - xA;
iyA = cy1 - yA;
thetaL = Math.atan(cx2/cy2);
xL = -r2*Math.sin(thetaL);
yL = -r2*Math.cos(thetaL);
ixL = cx2 - xL;
iyL = cy2 - yL;
if(d === 0 && r1 === r2) {
/* infinite solutions. circles are overlapping */
return [[ixA,iyA], [ixL,iyL]];
}
if (d < Math.abs(r1 - r2)) {
/* no solution. one circle is contained in the other */
return [[ixA,iyA], [ixL,iyL]];
}
/* 'point 2' is the point where the line through the circle intersection points crosses the line between the circle centers. */
/* Determine the distance from point 0 to point 2. */
a = ((r1*r1) - (r2*r2) + (d*d)) / (2.0 * d);
/* Determine the coordinates of point 2. */
x2 = cx1 + (dx * a/d);
y2 = cy1 + (dy * a/d);
/* Determine the distance from point 2 to either of the intersection points. */
h2 = r1*r1 - a*a;
h = lessThanEpsilon(h2) ? 0 : Math.sqrt(h2);
/* Now determine the offsets of the intersection points from point 2. */
rx = -dy * (h/d);
ry = +dx * (h/d);
/* Determine the absolute intersection points. */
var xi = x2 + rx, yi = y2 + ry;
var xi_prime = x2 - rx, yi_prime = y2 - ry;
return [[xi, yi], [xi_prime, yi_prime]];
}
function plot() {
var cases = [
{a: 1.1, l: 1.9, f: 0.3073},
{a: 1.0, l: 1.7, f: 0.3229}
];
var testCase = 1;
var magA = cases[testCase].a, magL = cases[testCase].l;
var maxS = Math.sqrt(magA*magA+magL*magL), magS1 = maxS * cases[testCase].f, magS2 = maxS - magS1;
var origin = [0,0], board = JXG.JSXGraph.initBoard('jxgbox', {boundingbox: [-5.0, 5.0, 5.0, -5.0], axis: true});
var drawAs = {dashed: {dash: 3, strokeWidth: 0.5, strokeColor: '#888888'} };
board.suspendUpdate();
var leftArm = board.create('slider', [[-4.5, 3], [-1.5, 3], [0, -64, -180]]);
var leftLeg = board.create('slider', [[-4.5, 2], [-1.5, 2], [0, -12, -30]]);
var rightArm = board.create('slider', [[0.5, 3], [3.5, 3], [0, 64, 180]]);
var rightLeg = board.create('slider', [[0.5, 2], [3.5, 2], [0, 12, 30]]);
var lh = board.create('point', [
function() { return +magA * Math.sin(deg2rad(leftArm.Value())); },
function() { return -magA * Math.cos(deg2rad(leftArm.Value())); }
], {size: 3, name: 'lh'});
var LA = board.create('line', [origin, lh], {straightFirst: false, straightLast: false, lastArrow: true});
var cLS1 = board.create('circle', [function() { return [lh.X(), lh.Y()]; }, function() { return magS1; }], drawAs.dashed);
var lf = board.create('point', [
function() { return +magL * Math.sin(deg2rad(leftLeg.Value())); },
function() { return -magL * Math.cos(deg2rad(leftLeg.Value())); }
], {size: 3, name: 'lf'});
var LL = board.create('line', [origin, lf], {straightFirst: false, straightLast: false, lastArrow: true});
var cLS2 = board.create('circle', [function() { return [lf.X(), lf.Y()]; }, function() { return magS2; }], drawAs.dashed);
var lx1 = board.create('point', [
function() { return circleIntersection(cLS1, cLS2)[0][0]; },
function() { return circleIntersection(cLS1, cLS2)[0][1]; }
], {size: 3, face:'x', name: 'lx1'});
var lx2 = board.create('point', [
function() { return circleIntersection(cLS1, cLS2)[1][0]; },
function() { return circleIntersection(cLS1, cLS2)[1][1]; }
], {size: 3, face:'x', name: 'lx2'});
/* Angle between lh, lx1 shall be between 0 and -180 */
var angleLAJ = board.create('text', [-3.7, 0.5, function(){ return angleBetween(lh, lx1).toFixed(2); }]);
/* Angle between lf, lx1 shall be between 0 and 180 */
var angleLLJ = board.create('text', [-2.7, 0.5, function(){ return angleBetween(lf, lx1).toFixed(2); }]);
var rh = board.create('point', [
function() { return +magA * Math.sin(deg2rad(rightArm.Value())); },
function() { return -magA * Math.cos(deg2rad(rightArm.Value())); }
], {size: 3, name: 'rh'});
var RA = board.create('line', [origin, rh], {straightFirst: false, straightLast: false, lastArrow: true});
var cRS1 = board.create('circle', [function() { return [rh.X(), rh.Y()]; }, function() { return magS1; }], drawAs.dashed);
var rf = board.create('point', [
function() { return +magL * Math.sin(deg2rad(rightLeg.Value())); },
function() { return -magL * Math.cos(deg2rad(rightLeg.Value())); }
], {size: 3, name: 'rf'});
var RL = board.create('line', [origin, rf], {straightFirst: false, straightLast: false, lastArrow: true});
var cRS2 = board.create('circle', [function() { return [rf.X(), rf.Y()]; }, function() { return magS2; }], drawAs.dashed);
var rx1 = board.create('point', [
function() { return circleIntersection(cRS1, cRS2)[1][0]; },
function() { return circleIntersection(cRS1, cRS2)[1][1]; }
], {size: 3, face:'x', name: 'rx1'});
var rx2 = board.create('point', [
function() { return circleIntersection(cRS1, cRS2)[0][0]; },
function() { return circleIntersection(cRS1, cRS2)[0][1]; }
], {size: 3, face:'x', name: 'rx2'});
var angleRAJ = board.create('text', [+1.3, 0.5, function(){ return angleBetween(rh, rx1).toFixed(2); }]);
var angleRLJ = board.create('text', [+2.3, 0.5, function(){ return angleBetween(rf, rx1).toFixed(2); }]);
board.unsuspendUpdate();
}
plot();
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/jsxgraph/0.99.7/jsxgraph.css" />
<link rel="stylesheet" href="style.css">
<script type="text/javascript" charset="UTF-8" src="//cdnjs.cloudflare.com/ajax/libs/jsxgraph/0.99.7/jsxgraphcore.js"></script>
</head>
<body>
<div id="jxgbox" class="jxgbox" style="width:580px; height:580px;"></div>
</body>
</html>
According to your sketch, the coordinates of E and G are:
E = (0, -magOE)
G = magOG * ( -sin(gamma), -cos(gamma) )
Then, calculating the position of H is a trilateration problem. Actually, it is just bilateration because you are missing a third distance. Hence, you will get two possible positions for H.
First, let us define a new coordinate system, where E lies at the origin and G lies on the x-axis. The x-axis direction in our original coordinate system is then:
x = (G - E) / ||G - E||
The y-axis is:
y = ( x.y, -x.x )
The coordinates of E and G in this new coordinate system are:
E* = (0, 0)
G* = (0, ||G - E||)
Now, we can easily find the coordinates of H in this coordinate system, up to the ambiguity mentioned earlier. I will abbreviate ||G - E|| = d like in the notation used in the Wikipedia article:
H.x* = (magGH * magGH - magEH * magEH + d * d) / (2 * d)
H.y* = +- sqrt(magGH * magGH - H.x* * H.x*)
Hence, we have two solutions for H.y, one positive and one negative.
Finally, we just need to transform H back into our original coordinate system:
H = x * H.x* + y * H.y* - (0, magOE)
Given the coordinates of H, calculating the angles is pretty straightforward:
alpha = arccos((H.x - G.x) / ||H - G||)
beta = arccos((H.y - E.y) / ||H - E||)
Example
Taking the values from your example
magOG = 3
magOE = 4
magGH = 3
magEH = 2
angleGamma = 82.74°
we first get:
E = (0, -4)
G = 3 * ( -sin(82.74°), -cos(82.74°) )
= (-2.976, -0.379)
Our coordinate system:
x = (-0.635, 0.773)
y = ( 0.773, 0.635)
In this coordinate system:
E* = (0, 0)
G* = (0, 4.687)
Then, the coordinates of H in our auxiliary coordinate system are:
H* = (2.877, +- 0.851)
I will only focus on the positive value for H*.y because this is the point that you marked in your sketch.
Transform back to original coordinate system:
H = (-1.169, -1.237)
And finally calculate the angles:
alpha = 25.41°
beta = 22.94°
The slight differences to your values are probably caused by rounding errors (either in my calculations or in yours).

Scale or Zoom X/Y pixels obtained from gps lat/long to fit a certain area

I'm looking for the right formula to transform a set of gps coordinats into X/Y pixels to represent dots in a given area.
The ultimate goal is to take a .gpx file for instance (or any array of lat/long pairs), parse it, and generate a png of the track/shape.
I don't care of the world's curbature and all the math that goes into getting the result to be as true to life as possible. Nor do I have to match the positions to a map. I just want the X/Y points to fit inside a given area expressed in pixels.
i have these fiddles:
https://jsfiddle.net/tpwyz2m5/20/
https://jsfiddle.net/bjugo7ff/
The problem is that they are to small, so small in fact that they all point to the same pixel in the given area. You have to multiply them with huge numbers and play around with them in order to get them to fit in the 320x240px area.
In some of the tests i ran i manged to blow up those numbers to get the result i wanted but when i tried it with a new set of coordinates, i got different result, meaning, the dots were to close or to far apart.
What i did in my tests (just to see if the conversion works) was to get the X/Y points from each lat/long. Looped through them to get the min/max values (those were the boundaries of the shape). Then i started multiplying and dividing numbers until i got the shape fit inside the given area.
so, this set of coordinates
var coords = {
0: {
lat: '45.88289847',
lon: '21.52251720'
},
1: {
lat: '45.88570661',
lon: '21.52431965'
},
2: {
lat: '45.88376483',
lon: '21.52770996'
}
};
and this
var coords = {
0: {
lat: '45.23621754',
lon: '20.49499512'
},
1: {
lat: '46.46813299',
lon: '21.67053223'
},
2: {
lat: '45.59097825',
lon: '22.74169922'
}
};
should have very similar result (just like in the fiddles).
here's the entire javascript i used in testing:
var coords = {
0: {
lat: '45.88289847',
lon: '21.52251720'
},
1: {
lat: '45.88570661',
lon: '21.52431965'
},
2: {
lat: '45.88376483',
lon: '21.52770996'
}
};
var w = document.getElementById('box').clientWidth; // 320px
var h = document.getElementById('box').clientHeight; // 240px
for (var key in coords) {
var lat = coords[key]['lat'];
var lon = coords[key]['lon'];
var x = (lon * w) / 360;
var y = (lat * h) / 180;
var div = document.getElementById('box');
var dot = document.createElement("div");
dot.className = 'dot';
dot.style.top = y+'px';
dot.style.left = x+'px';
div.appendChild(dot);
console.log(key + ' -> lat:' + lat + '; lon:' + lon + '; x: ' + x + '; y: ' + y);
}
html:
<div id="box"></div>
css:
#box {
width:320px;
height:240px;
border:1px solid #F00;
position:relative;
}
.dot {
width:2px;
height:2px;
background-color:#00F;
position:absolute;
}
so the question is, how do i scale or zoom in to fit the dots inside X by Y pixel rectangle.
After some thinkering I came up with this. I'm sure it's not the right way to do it, but it gets the job done. If anyone knows of a better (shorter more elegant) way, please share!
fiddle here:
https://jsfiddle.net/g1qtzqhj/
and the javascript as follows:
var div = document.getElementById('box');
var w = div.clientWidth;
var h = div.clientHeight;
var s = 1;
var d = 10;
var min_x = min_y = max_x = max_y = 0;
for (var key in coords) {
var lat = coords[key]['lat'];
var lon = coords[key]['lon'];
var x = (lon * w) / 360;
coords[key]['x'] = x;
var y = (lat * h) / 180;
coords[key]['y'] = y;
if (x < min_x || min_x == 0)
min_x = x;
if (x > max_x || max_x == 0)
max_x = x;
if (y < min_y || min_y == 0)
min_y = y;
if (y > max_y || max_y == 0)
max_y = y;
}
var dif_x = max_x - min_x;
var dif_y = max_y - min_y;
var bmax_x = bmax_y = 0;
for (var key in coords) {
var x = coords[key]['x'] = (coords[key]['x'] - (max_x - dif_x));
var y = coords[key]['y'] = (coords[key]['y'] - (max_y - dif_y));
if (x > bmax_x || bmax_x == 0)
bmax_x = x;
if (y > bmax_y || bmax_y == 0)
bmax_y = y;
}
nw = (bmax_x) * s;
nh = (bmax_y) * s;
r_x = w / bmax_x;
r_y = h / bmax_y;
for (var key in coords) {
var dot = document.createElement('div');
dot.className = 'dot';
dot.style.width = d +'px';
dot.style.height = d +'px';
dot.style.borderRadius = d +'px';
dot.style.top = h - (coords[key]['y'] * s * r_y) - (d / 2) + 'px';
dot.style.left = (coords[key]['x'] * s * r_x) - (d / 2) + 'px';
div.appendChild(dot);
}
I might have to rethink it (after I get some sleep) because looping through the same object three times does not seem right at all!

How to detect if a user has drawn a circle on a touch device using canvas and javascript?

I am creating a Tangram puzzle game using Javascript. And I need to detect when a user has drawn a circle (or circle like shape) with their finger. I have been able to gather hundreds (if not thousands) of x and y points with:
var touchX = event.targetTouches[0].pageX - canvas.offsetLeft;
var touchY = event.targetTouches[0].pageY - canvas.offsetTop;
I then push each x and y coordinate into an array:
touchMoveX.push(touchX);
touchMoveY.push(touchY);
I then loop through each array and create two points:
for(var i = 0; i < touchMoveX.length; i++)
{
for(var l=0; l < touchMoveY.length; l++)
{
var xPosition = touchMoveX[i];
var yPosition = touchMoveY[l];
var v1x = touchMoveX[i];
var v2x = touchMoveX[i + 1];
var v1y = touchMoveY[l];
var v2y = touchMoveY[l + 1];
Then using those two points, I use the following formula to figure out the angle between these two points in degrees:
var v1 = {x: v1x, y: v1y}, v2 = {x: v2x, y: v2y},
angleRad = Math.acos( (v1.x * v2.x + v1.y * v2.y) /
(Math.sqrt(v1.x*v1.x + v1.y*v1.y) * Math.sqrt(v2.x*v2.x + v2.y*v2.y) ) ),
angleDeg = angleRad * 180 / Math.PI;
I then sum up all of the angles and see if they are around 360 degrees.
But the above code I have described isn't working very well. Does someone out there have a better way to do this? Thank you very much.
yeah compute the average of all points (giving you a cheaply approximated center) then check if more than a certain percent of points are within a certain threshold. You can tune those values to adjust the precision until it feels right.
edit: Didn't consider that the circle could have multiple sizes, but you could just add another step computing the average of all distances. Adjusted the example for that.
var totalAmount = touchMoveX.length;
// sum up all coordinates and divide them by total length
// the average is a cheap approximation of the center.
var averageX = touchMoveX.reduce( function ( previous, current) {
return previous + current;
} ) / totalAmount ;
var averageY = touchMoveY.reduce( function ( previous, current) {
return previous + current;
} ) / totalAmount ;
// compute distance to approximated center from each point
var distances = touchMoveX.map ( function ( x, index ) {
var y = touchMoveY[index];
return Math.sqrt( Math.pow(x - averageX, 2) + Math.pow(y - averageY, 2) );
} );
// average of those distance is
var averageDistance = distances.reduce ( function ( previous, current ) {
return previous + current;
} ) / distances.length;
var min = averageDistance * 0.8;
var max = averageDistance * 1.2;
// filter out the ones not inside the min and max boundaries
var inRange = distances.filter ( function ( d ) {
return d > min && d < max;
} ).length;
var minPercentInRange = 80;
var percentInRange = inRange.length / totalAmount * 100;
// by the % of points within those boundaries we can guess if it's circle
if( percentInRange > minPercentInRange ) {
//it's probably a circle
}

Categories

Resources