I'm trying to rotate a DIV element using the mousemove event on an HTML page. I want to find a rotation angle using the dot product. I know that it's possible using Math.atan2 too, but I'd like to use dot product in my example.
So far, I tried to implement the following formula:
cos(angle) = dot(a, b) / (length(a) * length(b))
But the below implementation doesn't work well.
What could be the issue?
Thanks
Codepen:
https://codepen.io/1rosehip/pen/qBrLYLo
const $box = document.getElementById('box');
const shapeRect = $box.getBoundingClientRect();
const shapeCenterX = shapeRect.x + shapeRect.width / 2;
const shapeCenterY = shapeRect.y + shapeRect.height / 2;
/**
* get vector magnitude
* #param {Array.<number>} v
* #return {number}
*/
const length = v => {
return Math.sqrt(v[0] ** 2 + v[1] ** 2);
};
/**
* dot product
* #param {Array.<number>} v1
* #param {Array.<number>} v2
* #return {number}
*/
const dot = (v1, v2) => {
return v1[0] * v2[0] + v1[1] * v2[1];
};
/**
* handle rotation
*/
document.addEventListener('mousemove', (evt) => {
// vector #1 - shape center
const centerVector = [shapeCenterX, shapeCenterY];
const centerVectorLength = length(centerVector);
// vector #2 - mouse position
const mouseVector = [evt.pageX, evt.pageY];
const mouseVectorLength = length(mouseVector);
// cos(angle) = dot(a, b) / (length(a) * length(b))
const radians = Math.acos(dot(centerVector, mouseVector) / (centerVectorLength * mouseVectorLength));
const degrees = radians * (180 / Math.PI);
const angle = (degrees + 360) % 360;
$box.style.transform = `rotate(${degrees}deg)`;
});
#box{
position: absolute;
background: #111;
left: 100px;
top: 100px;
width: 100px;
height: 100px;
}
<div id="box"></div>
I've found the issues.
(1) The vectors were defined from the wrong origin (top left corner of the page instead of the shape center).
(2) Math.acos returns results in the range range [0,pi] instead of [0,2*pi].
It should be fixed by (360 - degrees) when the mouse moves to the left and passes the shape center.
The codepen with fixed version:
https://codepen.io/1rosehip/pen/JjWwaYE
const $box = document.getElementById('box');
const shapeRect = $box.getBoundingClientRect();
const shapeCenterX = shapeRect.x + shapeRect.width / 2;
const shapeCenterY = shapeRect.y + shapeRect.height / 2;
/**
* get vector magnitude
* #param {Array.<number>} v
* #return {number}
*/
const length = v => {
return Math.sqrt(v[0] ** 2 + v[1] ** 2);
};
/**
* dot product
* #param {Array.<number>} v1
* #param {Array.<number>} v2
* #return {number}
*/
const dot = (v1, v2) => {
return v1[0] * v2[0] + v1[1] * v2[1];
};
/**
* handle rotation
*/
document.addEventListener('mousemove', (evt) => {
// vector #1 - shape center
const centerVector = [evt.pageX - shapeCenterX, 0 - shapeCenterY];
const centerVectorLength = length(centerVector);
// vector #2 - mouse position
const mouseVector = [evt.pageX - shapeCenterX, evt.pageY - shapeCenterY];
const mouseVectorLength = length(mouseVector);
// cos(angle) = dot(a, b) / (length(a) * length(b))
const radians = Math.acos(dot(centerVector, mouseVector) / (centerVectorLength * mouseVectorLength));
let degrees = radians * (180 / Math.PI);
// const angle = (degrees + 360) % 360;
if(evt.pageX < shapeCenterX){
degrees = 360 - degrees;
}
$box.style.transform = `rotate(${degrees}deg)`;
});
#box{
position: absolute;
background: #111;
left: 100px;
top: 100px;
width: 100px;
height: 100px;
}
<div id="box"></div>
For two points:
{ lat: -6.346640110015869, lng: -39.293800354003906 }
{ lat: 66.450861, lng: 143.261551 }
Using popular method:
getDistanceBetweenPoints(location1: ILocation, location2: ILocation): number {
const lat1: number = location1.lat,
lng1: number = location1.lng,
lat2: number = location2.lat,
lng2: number = location2.lng,
R = 6378137,
dLat: number = this.toRadians(lat2 - lat1),
dLong: number = this.toRadians(lng2 - lng1),
const a: number =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(this.toRadians(lat1)) *
Math.cos(this.toRadians(lat1)) *
Math.sin(dLong / 2) *
Math.sin(dLong / 2);
// temp helpers
const xx = Math.sqrt(1 - a); ----> a = 1.339 => sqrt(-number) = NaN
const yy = Math.sqrt(a);
const zz = Math.atan2(xx, yy);
const c: number = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance: number = (R * c) / 1000;
I found problem in debug mode:
a > 1
Math.sqrt(1 - a) -> sqrt(<0) = NaN
It means, that a cannot be > 1.
But how to fix that?
map screen
I hope this will work (based on the Wikipedia article):
const RADIUS_OF_EARTH = 6378137;
const from = { lat: -6.346640110015869, lng: -39.293800354003906 };
const to = { lat: 66.450861, lng: 143.261551 };
const distance = (from, to) => {
const latF = from.lat / 180 * Math.PI;
const lngF = from.lng / 180 * Math.PI;
const latT = to.lat / 180 * Math.PI;
const lngT = to.lng / 180 * Math.PI;
const latD = Math.abs(latF - latT);
const lngD = Math.abs(lngF - lngT);
const latH = Math.pow(Math.sin(latD / 2), 2);
const lngH = Math.pow(Math.sin(lngD / 2), 2);
const delta = 2 * Math.asin(Math.sqrt(latH + Math.cos(latF) * Math.cos(latT) * lngH));
return RADIUS_OF_EARTH * delta;
};
console.log(distance(from, to));
I am trying to make a rotating zooming recursive golden triangle. It draws a golden triangle, then it draws another one inside it and so on. This was easy, but the challenge is making it zoom in and rotate around the point that the triangles are approaching.
To make it zoom in on that point infinitely I need to come up with the formula to calculate which point the triangles are approaching.
Running demo at this point: https://waltari10.github.io/recursive-golden-triangle/index.html
Repository: https://github.com/Waltari10/recursive-golden-triangle
/**
*
* #param {float[]} pivot
* #param {float} angle
* #param {float[]} point
* #returns {float[]} point
*/
function rotatePoint(pivot, angle, point)
{
const s = Math.sin(angle);
const c = Math.cos(angle);
const pointOriginX = point[0] - pivot[0];
const pointOriginY = point[1] - pivot[1];
// rotate point
const xNew = (pointOriginX * c) - (pointOriginY * s);
const yNew = (pointOriginX * s) + (pointOriginY * c);
const newPoint = [
pivot[0] + xNew,
pivot[1] + yNew,
]
return newPoint;
}
// https://www.onlinemath4all.com/90-degree-clockwise-rotation.html
// https://stackoverflow.com/questions/2259476/rotating-a-point-about-another-point-2d
// Position is half way between points B and C 72 and 72, because AB/BC is golden ratio
function drawGoldenTriangle(pos, height, rotation, color = [0,255,0,255], pivot) {
// golden triangle degrees 72, 72, 36
// golden gnomon 36, 36, 108
// AB/BC is the golden ratio number
// https://www.mathsisfun.com/algebra/sohcahtoa.html
const baseLength = (Math.tan(degToRad(18)) * height) * 2;
const pointA = rotatePoint(pos, rotation, [pos[0], pos[1] - height]); // sharpest angle
const pointB = rotatePoint(pos, rotation, [pos[0] - (baseLength / 2), pos[1]]);
const pointC = rotatePoint(pos, rotation, [pos[0] + (baseLength / 2), pos[1]]);
drawTriangle(pointA, pointB, pointC, [0,255,0,255]);
}
let i = 0;
function drawRecursiveGoldenTriangle(pos, height, rotation, pivot) {
drawGoldenTriangle(pos, height, rotation, [0,255,0,255], pivot);
i++;
if (i > 10) {
return;
}
const hypotenuseLength = height / Math.cos(degToRad(18));
const baseLength = (Math.tan(degToRad(18)) * height) * 2;
const goldenRatio = hypotenuseLength / baseLength;
const newHeight = height / goldenRatio;
const newRotation = rotation - 108 * Math.PI/180
const newPointC = rotatePoint(pos, rotation, [pos[0] + (baseLength / 2), pos[1]]);
// Go half baselength up CA direction from pointC to get new position
const newHypotenuseLength = baseLength;
const newBaseLength = newHypotenuseLength / goldenRatio;
let newPosXRelative = Math.cos(newRotation) * (newBaseLength / 2)
let newPosYRelative = Math.sin(newRotation) * (newBaseLength / 2)
const newPos = [newPointC[0] + newPosXRelative, newPointC[1] + newPosYRelative];
drawRecursiveGoldenTriangle(newPos, newHeight, newRotation, [0,255,0,255], pivot);
}
let triangleHeight = height - 50;
let pivotPoint = [(width/2),(height/2) -50];
let triangleLocation = [width/2, height/2 + 300];
let triangleRotation = 0;
function loop() {
i = 0;
const startTime = Date.now()
wipeCanvasData();
// triangleHeight++;
// triangleRotation = triangleRotation + 0.005;
// drawX(pivotPoint)
// drawX(triangleLocation)
// Pivot point determines the point which the recursive golden
// triangle rotates around. Should be the point that triangles
// approach.
drawRecursiveGoldenTriangle(triangleLocation, triangleHeight, triangleRotation, pivotPoint);
updateCanvas()
const renderTime = Date.now() - startTime
timeDelta = renderTime < targetFrameDuration ? targetFrameDuration : renderTime
this.setTimeout(() => {
loop()
}, targetFrameDuration - renderTime)
}
loop()
What would be the formula to calculate the point that recursive golden triangle is approaching? Or is there some clever hack I could do in this situation?
The starting point of the logarithmic spiral is calculated by startingPoint(a,b,c) where a,b,c are the points of your triangle:
The triangle in the snippet is not a proper 'golden triangle' but the calculations should be correct...
const distance = (p1, p2) => Math.hypot(p2.x - p1.x, p2.y - p1.y);
const intersection = (p1, p2, p3, p4) => {
const l1A = (p2.y - p1.y) / (p2.x - p1.x);
const l1B = p1.y - l1A * p1.x;
const l2A = (p4.y - p3.y) / (p4.x - p3.x);
const l2B = p3.y - l2A * p3.x;
const x = (l2B - l1B) / (l1A - l2A);
const y = x * l1A + l1B;
return {x, y};
}
const startingPoint = (a, b, c) => {
const ac = distance(a, c);
const ab = distance(a, b);
const bc = distance(b, c);
// Law of cosines
const alpha = Math.acos((ab * ab + ac * ac - bc * bc) / (2 * ab * ac));
const gamma = Math.acos((ac * ac + bc * bc - ab * ab) / (2 * ac * bc));
const delta = Math.PI - alpha / 2 - gamma;
// Law of sines
const cd = ac * Math.sin(alpha / 2) / Math.sin(delta);
const d = {
x: cd * (b.x - c.x) / bc + c.x,
y: cd * (b.y - c.y) / bc + c.y
};
const e = {
x: (a.x + c.x) / 2,
y: (a.y + c.y) / 2
};
const f = {
x: (a.x + b.x) / 2,
y: (a.y + b.y) / 2,
};
return intersection(c, f, d, e);
};
d3.select('svg').append('path')
.attr('d', 'M 100,50 L150,200 H 50 Z')
.style('fill', 'none')
.style('stroke', 'blue')
const point = startingPoint({x: 50, y: 200},{x: 100, y: 50},{x: 150, y: 200});
console.log(point);
d3.select('svg').append('circle')
.attr('cx', point.x)
.attr('cy', point.y)
.attr('r', 5)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="200" height="400"></svg>
I'm trying to implement a function that returns some predefined values with a particular probability.
For people knowing Python's NumPy module, I suppose I'm wondering whether there is an equivalent to numpy.random.choice() in JavaScript?
If not, how else one could go about this efficiently?
Here my code:
// outcomes
const max_outcome = 10;
const min_outcome = -10;
let x1 = Math.floor(Math.random() * (max_outcome - min_outcome + 1) + min_outcome);
let y1 = Math.floor(Math.random() * (max_outcome - min_outcome + 1) + min_outcome);
// probabilities
const max_proba = 1;
const min_proba = 0.5;
let p1 = Math.random() * (max_proba - min_proba) + min_proba;
p1 = Math.round(p1 * 100) / 100;
let p2 = 1 - p1;
p2 = Math.round(p2 * 100) / 100;
function return_value(x1, y1, p1, p2) {
// should randomly return x1 with p1 probability or y1 or p2 probability}
Any ideas/thoughts would be gratefully received.
I need to work out what is the volume of liquid left in a cylinder on its side in JavaScript, how will I do the following in code?
You can try something like this.I have used Math.acos and Math.pow.And rest is simple Mathematics.
Since Math.acos returns NaN if the number is not between (-1 and 1)
,so I have checked before if the acos returns NaN
function volume(diameter, depth, length) {
let R = diameter / 2;
if (Math.acos((R - depth )/ R) != NaN) {
let a = Math.pow(R, 2) * Math.acos((R - depth) / R) - (R - depth) * (Math.pow((2 * R * depth - Math.pow(depth, 2)), 0.5))
return a * length;
} else {
return "Cylinder radius can't be less than depth"
}
}
// returns volume in meter cube
// 1 meter cube =1000l
console.log(volume(1.08, 0.72, 2.40)*1000,"L")
You can use:
** operator for powers (or Math.pow)
Math.acos for cos^(-1)
Math.sqrt for the square root
console.log(calculateVolumeInCylinder(1.08, 2.4, 0.72))
/**
* #param {number} Dm - Cylinder diameter in meters.
* #param {number} L - Cylinder length in meters.
* #param {number} Dp - Depth in meters.
* #returns {number} Volume in liters.
*/
function calculateVolumeInCylinder(Dm, L, Dp) {
let R = Dm / 2,
// R^2 cos^-1(R-D/R)
sA = R ** 2 * Math.acos((R - Dp) / R),
// (R-D)
sB = (R - Dp),
// SQRT(2RD-D^2)
sC = Math.sqrt(2 * R * Dp - Dp ** 2);
return (L * (sA - sB * sC)) * 1000;
}
Hi Shubh and Matt Major Thanks!!!! i manage to do it via the following.
function round(d)
// Returns a number rounded to 4 decimal places.
{ var multiplier = 10000;
return Math.round(d*multiplier) / multiplier;
};
function negative(n)
{ if(n<0)
complain("Negative input");
return (n<0);
}
function calculate(vdiam,vlen,vdepth){
//var vdiam = 1.08;
//var vlen = 2.40;
//var vdepth = 0.72;
var res = 0; //result
//Convert inputs to numbers
d = new Number(vdiam);
l = new Number(vlen);
h = new Number(vdepth);
r = d/2;
if(negative(d)) return;
if(negative(l)) return;
if(negative(h)) return;
//make sure it's all kosher
if(h>d)
{ console.log("Depth exceeds diameter");
return;
}
//calculate
var segArea =r*r*Math.acos((r-h)/r) - (r-h)*Math.sqrt(2*r*h-h*h);
res = segArea*l;
if(isNaN(res))
{ console.log("Inputs must be positive numbers");
res = "";
return;
}
res = res*1000;
return round(res);
}
alert(calculate(1.08,2.40,0.72));