Need help understanding mathematical formula for ellipse in JavaScript - javascript

Need help understanding this code, which is used to make an image move in an elliptical shape. What I don't understand, is the formula for e, px and py variable. What exactly is the e variable defined the complex way it is? I know it uses some mathematical formulas but i don't know which ones.
var b = 125;
var h = 115;
var rx = 7;
var ry = 4;
var e = 0;
function update() {
setInterval(function() {
e = (e + Math.PI / 360) % (Math.PI * 2);
rotate(e);
}, 10);
var lyd = new Audio("Vedlegg/skoytelyd.mp3");
lyd.play();
}
function rotate(e) {
var px = b + rx * Math.cos(e)*b/2;
var py = h + ry * Math.sin(e)*h/2;
document.getElementById("punkt").style.left = px + "px";
document.getElementById("punkt").style.top = py + "px";
}
</script>
<style>
div {
position: fixed;
}
#sentrum {
background: black;
left: 100px;
top: 50px;
}
#skoyteloper {
position: absolute;
top: 190px;
left: 450px;
width: 60px;
height: 60px;
}
</style>
</head>
<body>
<div id="sentrum"></div>
<img src="Vedlegg/bane.jpg" id="imgBane"></img>
</div>
<div id="punkt">
<img src="Vedlegg/skoyteloper.png" id="skoyteloper"></img>
</div>
TILBAKE
</body>

Sinus and cosinus:
Think of a triangle with one 90° angle. 1 line is horizontal, another line is at the right side and is vertical, the third line goes from bottom-left to top-right.
The angle on the left we call e (yes,it is the e in your function)
sinus of e is defined as the vertical line on the right divided by the diagonal line; cosinus e = horizontal line (which is touching the angle e) / diagonal.
--
Now draw a circle with middle at angle e and radius = the length of the diagonal.
If you raise the angle e you will see the vertical line get bigger and the horizontal line get smaller (keep the length of the diagonal constant), until you reach 90°. Then of course you can go beyond 90°, then the vertical line can be on the left. Further than 180° the vertical line will point down (negative coordinate), ...
So that's one of the uses of sin and cos: if you set an angle they give you an y-value and a x-value, showing you 1 point on a circle. It's always a number between -1 and +1. example: sin(0) = 0 (no vertical component), cos(0) = 1
This code here below gives you a circle around center (0,0) and radius 100. Feed this function a bunch of values for e and you get as many points on a circle
function rotate(e) {
var px = 100 * Math.cos(e);
var py = 100 * Math.sin(e);
}
Now, if instead of 100 * cos(e) you put 200 * cos(e), then it's not a circle anymore. Every x coordinate will be twice as far (compared to the circle). A different rx and ry will result in an ellipse.
your variables b & h are for pushing the center of the ellipse to somewhere inside the image/div/canvas/... rather than in a corner (then you clip most of the ellipse).
Does this help?

Related

Animating SVG: Move shape in direction of mouse/rotate around fixed point?

After looking through similar questions posted to the forum and not finding something that helped me solve my own problem, I'm posting it.
I'm using SVG.js to generate SVG shapes in a web document. I'd like one of those shapes to ”follow” the mouse/cursor.
By that I mean: The shape has a fixed position/anchor point (at its original center) and it can only move a limited distance (let's say 50px) away from this fixed point.
I want the shape to move in the direction of the cursor, whenever the cursor moves, but never further than a defined distance away from its orignal position. I'm attaching a short animation to illustrate my description:
If the cursor were to disappear, the shape would snap back to its original center.
I know my way around Javascript, HTML and CSS. This type of element-manipulation is new to me and the math is giving my quite the headache, any help would be great.
It looks like I need the shape to basically rotate around its original center, with an angle relative to the cursor? I'm really unsure how to solve this. I have tried using a method to calculate the angle described in this post. My shape moves, but not as intended:
// init
var draw = SVG().addTo('body')
// draw
window.shape = draw.circle(25, 25).stroke({
color: '#000',
width: 2.5
}).fill("#fff");
shape.attr("id", "circle1");
shape.move(50, 50)
// move
var circle = $("#circle1");
var dist = 10;
$(document).mousemove(function(e) {
// angle
var circleCenter = [circle.offset().left + circle.width() / 2, circle.offset().top + circle.height() / 2];
var angle = Math.atan2(e.clientX - circleCenter[0], -(e.clientY - circleCenter[1])) * (180 / Math.PI);
var x = Math.sin(angle) * dist;
var y = (Math.cos(angle) * dist) * -1;
shape.animate().dmove(x, y);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/3.0.16/svg.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Note: It does not matter to me whether the solution depends on jQuery or not (ideally it doesn't).
After more fiddling around with some solutions to calculating angles and distances, I found the answer.
I'm using a fixed reference point to calculate the angle of the direct line between the center of the shape and the cursor. Then I move the shape relative to this reference point and by a given amount:
// Init canvas
var draw = SVG().addTo('body')
// Draw reference/anchor
var shape_marker_center = draw.circle(3,3).fill("#f00").move(150, 150);;
var grafikCenter = [shape_marker_center.attr("cx"), shape_marker_center.attr("cy")]
// Draw shapes
var shape = draw.circle(25, 25).stroke({color: '#000', width: 2.5 }).fill("none");
shape.attr("id", "circle1").attr({cx: grafikCenter[0], cy:grafikCenter[1]})
var shape2 = draw.circle(50, 50).stroke({color: '#000', width: 2.5 }).fill("none");
shape2.attr("id", "circle2").attr({cx: grafikCenter[0], cy:grafikCenter[1]})
var shape3 = draw.circle(75, 75).stroke({color: '#000', width: 2.5 }).fill("none");
shape3.attr("id", "circle3").attr({cx: grafikCenter[0], cy:grafikCenter[1]})
$(document).mousemove(function(e) {
var pointA = [shape_marker_center.attr("cx"), shape_marker_center.attr("cy")];
var pointB = [e.clientX, e.clientY];
var angle = Math.atan2(pointB[1] - pointA[1], pointB[0] - pointA[0]) * 180 / Math.PI ;
//
var distance_x_1 = Math.cos(angle*Math.PI/180) * 16;
var distance_y_1 = Math.sin(angle*Math.PI/180) * 16;
var distance_x_2 = Math.cos(angle*Math.PI/180) * 8;
var distance_y_2 = Math.sin(angle*Math.PI/180) * 8;
//
shape.center((grafikCenter[0] + distance_x_1), (grafikCenter[1] + distance_y_1));
shape2.center((grafikCenter[0] + (distance_x_2) ), (grafikCenter[1] + (distance_y_2)));
})
svg {
width: 100vw;
height: 100vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/3.0.16/svg.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

How do I get the correct degress from style.transform rotate after I converted the matrix values?

I am trying to get the rotation degree from div's I rotate to create a line pattern.
Only now I am running into a problem. I need the rotation(deg) from the div's to calculate where the next line needs to appear. But when I try to get the value from a div with style.transform and convert the matrix values I still get the wrong degrees.
In my testing case I have a div that is rotated 150deg, but I get 30 deg back and this will not work for me unfortunatly. Please help, how do I get the 150deg value back?
Here is the code:
HTML:
<div class="linesBox" id="linesBox">
<div class="line1 lines" id="line1" style="float: left;
margin: 200px 0 0 100px;
position: fixed;
border-top:0.5px solid black;
width: 100px;
transform: rotate(150deg);"></div>
<!-- <div class="line2 lines" id="line1" style="float: left;
margin: 174px 0 0 193.3px;
position: fixed;
border-top:0.5px solid black;
width:100px;
transform: rotate(0deg);"></div> -->
</div>
JavaScript:
const linesBox = document.getElementById('linesBox');
let lastLineWidth;
let angle;
let lineWidthCount;
let lineHeightCount;
function createLines(){
//Last line div in a var and a var to get the computated value
let lastLine = linesBox.lastElementChild;
var st = window.getComputedStyle(lastLine, null);
//Get the width and angle of the last line and place them in a var
lastLineWidth = parseFloat(lastLine.style.width);
lastLineXPos = parseFloat(lastLine.style.marginLeft);
lastLineYPos = parseFloat(lastLine.style.marginTop);
let lastLineAngle = st.getPropertyValue("transform");
console.log(lastLineWidth, lastLineXPos, lastLineYPos);
//Get and map the Matrix value from transform rotate() and set it to lastLineAngle
var values = lastLineAngle.split('(')[1],
values = values.split(')')[0],
values = values.split(',');
//Set each value of the matrix values to a var
let a = values[0];
let b = values[1];
let c = values[2];
let d = values[3];
//Take the correc value from the matrix values and place it in the formula and save the outcome in a var
angle = Math.round(Math.asin(b) * (180/Math.PI));
console.log(angle);
//Calculate margin left starting position for the next line
//Get sin en cos values by angle
let yChangeOne = lastLineWidth * Math.sin(angle / 180 * Math.PI) / 2;
let xChangeOne = parseFloat(lastLineWidth - lastLineWidth * Math.cos(angle / 180 * Math.PI)) / 2;
// let yChangeOne = lastLineWidth * sinOutcome / 2;
// let xChangeOne = lastLineWidth - lastLineWidth * cosOutcome;
console.log(yChangeOne, xChangeOne);
let newYPos;
let newXPos;
if( angle <= 89 && angle >= 1 ){
newYPos = lastLineYPos + yChangeOne;
} else if ( angle >= 91 && angle <= 179 ){
newYPos = lastLineYPos - yChangeOne;
}
console.log(newYPos);}
//Get the start position for the next line
createLines();
The angle should return 150deg not 30deg otherwise my if statement will not work. Please help :)
Both 30° and 150° have the same sine. You also need to take the cosine into account. Instead of Math.asin(b), use
Math.atan2(b, a)
Btw, if you are just calculating the angle to calculate its sine and cosine again, then spare this step (Math.sin(angle...)). You have sine and cosine right there, so just use them.

JS display text tangent to a circle

Here is what I have so far
https://jsfiddle.net/0p7zf13x/5/
Here is what I would like to obtain
Text around circle, and tangent to its center
The code is on jsfiddle, but here is a little piece of it.
var count = $("#rollo ul li").length;
var cx = 300;
var cy = 300;
var r = 300;
$("#rollo ul li").each(function(index) {
var theta = 2 * Math.PI * (index / count);
var left = cx + r * Math.sin(theta);
var top = cy - r * Math.cos(theta);
console.log(index, left, top);
$(this).css({ left: left, top: top });
$(this).css({"transform":"rotate("+Math.cos(theta)*-90+"deg)"});
});
I started from this code page
placing divs in a circle
Question : How to make texts around circle and left side of first letter, tangent to that same circle ? All texts evenly distributed.
Thanks
Just use the angle you're generating from:
$(this).css({"transform":"rotate("+(theta-Math.PI/2)+"rad)"});
fiddle

Find new left and top corresponding to original left and top on CSS rotate

Suppose my div has left:200px and top:400px, after I apply a rotate transform of suppose 90 deg the above top and left positions no more point to the old positions. Now how can we calculate the new top and left for the transformed div which are equivalent to the left and top positions of the non-transformed div after rotation.
Edited answer
Besides the starting position of the corner point (top-left in your example), and the rotation angle, we also need to know the position of the reference point of the rotation. This is the point around which we rotate the div (CSS calls it transform-origin). If you don't specify it, then normally, the centre of mass of the element is used.
I don't know of any JavaScript method that simply calculates it for you, but I can show you its Math, and a simple JS implementation.
Math
P: original position of the corner point, with (Px, Py) coordinates
O: reference point of the rotation, with (Ox, Oy) coordinates
Calculate the original position of P, relative to O.
x = Px - Ox
y = Py - Oy
Calculate the rotated position of P, relative to O.
x' = x * cos(angle) - y * sin(angle)
y' = x * sin(angle) + y * cos(angle)
Convert this position back to the original coordinate system.
Px' = x' + Ox
Py' = y' + Oy
If you're not aware of the formulas in step #2, you can find an explanation here.
JavaScript implementation
function rotatedPosition(pLeft, pTop, oLeft, oTop, angle){
// 1
var x = pLeft - oLeft;
var y = pTop - oTop;
// 2
var xRot = x * Math.cos(angle) - y * Math.sin(angle);
var yRot = x * Math.sin(angle) + y * Math.cos(angle);
// 3
var pLeftRot = xRot + oLeft;
var pTopRot = yRot + oTop
return {left: pLeftRot, top: pTopRot};
}
rotatedPosition requires you to define the original position of the point and the reference point, plus the angle.
In case you need a method which takes only a single argument, the div element itself, and computes the rest for you, then you can do something like:
function divTopLeftRotatedPosition(div){
var pLeft = // ...
var pTop = // ...
var width = // ...
var height = // ...
var angle = // ...
return rotatedPosition(pLeft, pTop, pLeft + width / 2, pTop + height / 2, angle);
}

Javascript Image Pan / Zoom

By combining some CSS and Jquery UI / draggable I have created the ability to pan an image and with a little extra JS you can now zoom the image.
The problem I am having is that, if you zoom in the image's top left corner is fixed, as you would expect. What I would like is for the image to stay central (based on the current pan) so that the middle of the image stays in the middle of the container whilst getting larger.
I have written some code for this but doesn't work, I expect my maths is wrong. Could anyone help?
I want it to work like this does. When you scroll into an image it keeps the image centered based on the current pan rather than zooming out from the corner.
HTML:
<div id="creator_container" style="position: relative; width: 300px; height: 400px; overflow: hidden;">
<img src="/images/test.gif" class="user_image" width="300" style="cursor: move; position: absolute; left: 0; top: 0;">
</div>
Javascript:
$("#_popup_creator .user_image").bind('mousewheel', function(event, delta) {
zoomPercentage += delta;
$(this).css('width',zoomPercentage+'%');
$(this).css('height',zoomPercentage+'%');
var widthOffset = (($(this).width() - $(this).parent().width()) / 2);
$(this).css('left', $(this).position().left - widthOffset);
});
Long story short, you need to make a transform matrix to scale by the same amount as the image and then transform the image's position using that matrix. If that explanation is complete greek to you, look up "image transforms" and "matrix math".
The beginning of this page is a pretty good resource to start with even though it's a different programming language:
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/geom/Matrix.html
Anyway, I've implemented those methods in some projects of my own. Here's the zoom in function from something I wrote that functions the way you want:
function zoomIn(event) {
var prevS = scale;
scale += .1;
$(map).css({width: (baseSizeHor * scale) + "px", height: (baseSizeVer * scale) + "px"});
//scale from middle of screen
var point = new Vector.create([posX - $(viewer).width() / 2, posY - $(viewer).height() / 2, 1]);
var mat = Matrix.I(3);
mat = scaleMatrix(mat, scale / prevS, scale / prevS);
point = transformPoint(mat, point);
//http://stackoverflow.com/questions/1248081/get-the-browser-viewport-dimensions-with-javascript
posX = point.e(1) + $(viewer).width() / 2;
posY = point.e(2) + $(viewer).height() / 2;
$(map).css({left: posX, top: posY});
return false;//prevent drag image out of browser
}
Note the commands "new Vector.create()" and "Matrix.I(3)". Those come from the JavaScript vector/matrix math library http://sylvester.jcoglan.com/
Then note "transformPoint()". That's one of the functions from that ActionScript link (plus hints on http://wxs.ca/js3d/) that I implemented using sylvester.js
For the full set of functions I wrote:
function translateMatrix(mat, dx, dy) {
var m = Matrix.create([
[1,0,dx],
[0,1,dy],
[0,0,1]
]);
return m.multiply(mat);
}
function rotateMatrix(mat, rad) {
var c = Math.cos(rad);
var s = Math.sin(rad);
var m = Matrix.create([
[c,-s,0],
[s,c,0],
[0,0,1]
]);
return m.multiply(mat);
}
function scaleMatrix(mat, sx, sy) {
var m = Matrix.create([
[sx,0,0],
[0,sy,0],
[0,0,1]
]);
return m.multiply(mat);
}
function transformPoint(mat, vec) {
return mat.multiply(vec);
}

Categories