p5.Vector not respecting angleMode - javascript

I've noticed that when rotating vectors, the rotation is done in radians even after I have called angleMode with DEGREES. This behaviour only appears with vectors declared using the new p5.Vector syntax, while using createVector avoids it.
Minimal reproducible example: (paste into p5 web editor or OpenProcessing)
function setup() {
createCanvas(400, 400)
translate(100, 100) // To see the effect more clearly
angleMode(DEGREES)
let v = new p5.Vector(1, 0)
line(0, 0, v.x * 50, v.y * 50)
v.rotate(HALF_PI)
line(0, 0, v.x * 50, v.y * 50)
}
Notice that the rotation is still done in radians. If you replace new p5.Vector with createVector then the problem disappears. Why is this?

Since mathematically functions like sin() are effected, there is no good reason why p5.Vector.rotate should not be effected.
However it is not intended to construct a vector like you did. You should use createVector() to create a vector. This function not only creates the object, but sets all internal attributes so that the objects behave as intended.
This behavior is not explained directly for rotate(), however it is documented for heading():
Calculate the angle of rotation for this vector(only 2D vectors). p5.Vectors created using createVector() will take the current angleMode into consideration, and give the angle in radians or degree accordingly.

Related

Is the Canvas2D ellipse() specification/implementation incorrect?

I've been playing around with how to render wireframe perspective-correct spheres using only canvas2d and ellipse math.
It's been fun, but I've soon come to realize that the ellipse() function has a very strange implementation with regards to the spec.
Indeed, the ellipse function takes 7 (or 8) arguments:
ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle)
The startAngle is described as such:
The angle at which the ellipse starts, measured clockwise from the positive x-axis and expressed in radians.
Given a parameter 0 <= t <= 2 * PI, we can compute the position of the associated point on the ellipse like so:
let dx = radiusX * cos(t)
let dy = radiusY * sin(t)
let px = x + dx * cos(rotation) - dy * sin(rotation)
let py = y + dx * sin(rotation) + dy * cos(rotation)
And if we use startAngle = t, our ellipse will begin its arc at our point. But. But. t is NOT an angle, and definitely not the angle of our point from the x-axis of the ellipse. Apparently some people still call it the eccentric angle, but my point still stands.
(See here)
And indeed, if we try to make the arc of an ellipse start at a specific angle, we can see that the result is not what we expect, unless the ellipse is a circle (radiusX = radiusY) or startAngle is a multiple of PI / 2.
Here is an interactive demo I've put up so that you can witness the strange default behaviour.
My claim is that the function should always behave like it does in the corrected case with the current specification.
Either that or change the spec to talk about parameters t for startAngle and endAngle, and avoid saying they are angles, because currently they definitely are not.
Does anyone know how this implementation/spec came to be, if anyone reported this before and if not where to lead such a discussion?
Any other insight appreciated!
I found this related question but it's rather unsatisfactory as replies merely show how to correct the function, but don't discuss whether the spec or implementation should be corrected.

How to smoothly track a center of mass?

I'm developing a program which tracks many small objects. They are very closely grouped for the most part.
My desire is to smoothly track the center of mass. The following code runs 60 times per second in all cases and calculates the center of mass:
let cX = 0;
let cY = 0;
for (let c of entities) {
cX += c.x;
cY += c.y;
}
prevCenterX = centerX;
prevCenterY = centerY;
centerX = cX / entities.length;
centerY = cY / entities.length;
That is, I simply add up all the positions of each entity and then divide by the entity count in order to get the x and y coordinate of the center of the mass of objects.
Then in the rendering code, I simply do this:
camera.x = lerp(prevCenterX, centerX, alpha);
camera.y = lerp(prevCenterY, centerY, alpha);
Where alpha is the percentage (from 0.0 to 1.0) of the tick that we are through (because the simulation only updates 60 times per second, but the user's monitor may have any sort of refresh rate). The rendering code in my case runs 165 times per second.
Anyways, this works perfectly in 99% of cases. The issue is that when an entity is either gained or lost, then the centerX and centerY very quickly change, which causes the camera to do a jarring jump the next frame, which is very visually unpleasant.
Is there a way to counteract this? My desire is for the camera to always move smoothly. I have been trying to figure out a solution to this for weeks now, but every one of my attempted solutions fails. Is this more tricky than I think, or am I missing something obvious?
You can try simple smoothing of center position, for example, with IIR filter:
centerX = centerX * (1-t) + t * cX / entities.length;
centerY = centerY * (1-t) + t * cY / entities.length;
where t is coefficient like 0.1 or 0.5 (depending on needed smoothing degree).
If result looks better, but is still unpleasant, consider using of Kalman filter. (More complex, but is widely used in object tracking. While math background looks very tangled, you can find consize implementations).

Sphere rotation in Babylon.js

I want to ask for help regarding rotation using the Babylon.js framework.
I need the sphere to rotate 45 degrees, exactly aligned with the diagonal circle, which has a 45 degree orientation, but I'm not getting it.
The code I made is in the link below:
https://codepen.io/polalas/pen/VwvaKwL
The method responsible for the rotation is the loop () method, which is triggered every time the scene is rendered.
function loop () {
var y1 = scene.getMeshByName("I1");
y1.rotation.y - = 0.01 * Math.sin (Math.PI / 4);
y1.rotation.x - = 0.01 * Math.sin (Math.PI / 4);
}
I imagine that I mishandled the rotation. Could someone help, please?
Using your code, the best way to achieve that is to first rotate the parent of the sphere (what you called newMesh (or I1)) 45 degrees around the Z axis right before adding the sphere as a child:
newMesh.rotate(BABYLON.Axis.Z, Math.PI / 4);
Afterwards you can rotate it around its Local (!) X axis in your render loop:
function loop(){
var y1 = scene.getMeshByName("I1");
}
This way you get a perfect rotation around your (mocked) pivot.

More elegant way to resize a sector path using Raphaël JS and SVG?

I am using Raphaël for the first time with little svg experience and I need someone who is really knowledgeable with these two to help me.
I have created a pie chart with dynamic sectors. The sectors can be resized by dragging on the round buttons. See this fiddle. I have only tested in Chrome and Safari which are the only required browsers.
The pie chart is not yet complete. The sectors can overlap. Please ignore this for now.
I was faced with problems, when the starting angle of a sector was greater than the ending angle. This is the case when the ending angle goes past the 0/360° mark. To solve this I made use of the path-rotation-parameter. I moved the sector forward while moving the angles back, until the end angle is at 360. You can see this in the fiddle in this function:
function sector_update(cx, cy, r, startAngle, endAngle, sec) {
var x1 = cx + r * Math.cos(-startAngle * rad),
x2 = cx + r * Math.cos(-endAngle * rad),
y1 = cy + r * Math.sin(-startAngle * rad),
y2 = cy + r * Math.sin(-endAngle * rad);
var rotation = 0;
// This is the part that I have the feeling could be improved.
// Remove the entire if-clause and let "rotation" equal 0 to see what happens
if (startAngle > endAngle) {
rotation = endAngle;
startAngle = startAngle - endAngle;
endAngle = 360;
}
sec.attr('path', ["M", cx, cy, "L", x1, y1, "A", r, r, rotation,
+(endAngle - startAngle > 180), 0, x2, y2, "z"]);
}
Although it works nicely, I'm a bit skeptical. Can this be solved without the rotation of the path? I appreciate any help or pointers.
Can this be solved without the rotation of the path?
Answer: Yes, it can. You don't have to change the rotation of the path at all. Unless I'm missing something, the following code seems to work the same as what you have in the fiddle:
function sector_update(cx, cy, r, startAngle, endAngle, sec) {
var x1 = cx + r * Math.cos(-startAngle * rad),
x2 = cx + r * Math.cos(-endAngle * rad),
y1 = cy + r * Math.sin(-startAngle * rad),
y2 = cy + r * Math.sin(-endAngle * rad);
//notice there is no "roation" variable
if (startAngle > endAngle) {
startAngle -= endAngle;
endAngle = 360;
}
sec.attr('path', ["M", cx, cy, "L", x1, y1, "A", r, r, 0,
+(endAngle - startAngle > 180), 0, x2, y2, "z"]);
}
Explanation: For my explanation, I will use the SVG terminology in the
W3 Spec and Raphael Reference Library. That is, while you use cx, cy, and rotation, these use rx, ry, and x-axis-rotation respectively.
In short, whenever rx equals ry, then x-axis-rotation is meaningless.
Look at this SVG. Use your browser's development tools, or save the SVG to your computer and use a file editor to edit it. Specifically, look at the last path element, which has four arcs in it. Try modifying the x-axis-rotation value on each arc. You will notice that the first arc (where rx and ry are both "25") never changes when you update x-axis-rotation value.
Why? This is because you have a circular arc. No matter how much you rotate a circle, it will still be the same circle. For example, hold up a glass in front of you so that the glass is horizontal to the ground, and you are looking directly down the glass. Now rotate/twist the glass with your wrist. Do you see how the circular shape you see stays in the same circular shape? Now set the glass on the table normally (so it is vertical and could hold a liquid). Now tip the glass over. You can see the obvious perspective change; it was pointing up, but now it is laying flat. That is what x-axis-rotation does.
Perhaps a better example is to just play around with the aforementioned SVG file. Play with x-axis-rotation on the arcs in the final path element. You will see the arcs being rotated around. That is what x-axis-rotation does.
Back to your code: Because you are dealing only with circular objects, the x-axis-rotation will make no difference on the final output. So long as you are only dealing with circular objects, you can hard-code it's value to zero without any worries. All you really needed to do is modify the angles, which you had done correctly.
Performance: I tried using JavaScript to time your sector_update function both with and without modifying the x-axis-rotation variable. The result? I saw no difference in performance. The majority of the time spent is on actually drawing the SVG, not on the math that determines it's values. In fact, all you are really doing in JavaScript is updating the code to set the value in the path element. At that point in time, the browser takes over with it's rendering engine to actually draw the SVG object. I suppose then it's a per-browser issue, as each browser has different rendering performance. But as for whether or not the x-axis-rotation value has any effect, my guess is no. If there is a performance hit (because the browser may have to do an additional floating-point operation), it is so incredibly moot because the overwhelming majority of the time is spent drawing the object, not calculating it's values. So I would say not to worry about it.
I hope that helps, let me know if I missed something or didn't explain something well enough.

My collision detection algo seems to trigger even before the objects touched

I wrote a very simple collision detection demo:
http://jsfiddle.net/colintoh/UzPg2/5/
As you can see, the objects sometimes doesn't connect at all but yet the collision is being triggered. The radius for the balls are 10px so the algo triggered the collision whenever the distance between two balls center is less than 20px. I reduced it to 18px for a better visual but the empty collision still happens randomly. Am I doing something wrong?
It looks like you are not using the right formula for distance between two points. See http://www.purplemath.com/modules/distform.htm for a full explanation.
You are doing this:
this.ballCollide = function(balli) {
if (Math.abs((this.x) - (balli.x)) < (2*radius - buffer)) {
if (Math.abs((this.y) - (balli.y)) < (2*radius - buffer)) {
// Do collision
}
}
};
That's a square bounding box, not a circular one. To get a circular bounding box, you can do something like this, based on the formula in the referenced web page:
this.ballCollide = function(balli) {
var deltax = this.x - balli.x;
var deltay = this.y - balli.y;
if (Math.sqrt(deltax * deltax + deltay * deltay) < 2 * radius - buffer) {
// Do collision
}
};
See http://jsfiddle.net/UzPg2/14/ for a working example.
Note that a perfect circular bounding box is a much slower algorithm than a square bounding box approximation.
Following Jarrod Roberson's point (a perfect circle is always inside a perfect square), you'd do that by basically combining your original code with the code I posted, like this (and you could combine them both into one conditional switch if you wanted to):
var deltax = this.x - balli.x;
var deltay = this.y - balli.y;
var dist = 2 * radius - buffer;
if (Math.abs(deltax) < dist && Math.abs(deltay) < dist) {
if (Math.sqrt(deltax * deltax + deltay * deltay) < dist) {
// Do collision
}
}
See http://jsfiddle.net/UzPg2/21/ for a working example (I've left the buffer as your variable is called at 2, but I personally think it looks better with a value of 1).
There are also many other ways you can optimize this for speed if you need to, but Jarrod's suggestion gives you the biggest immediate speed boost.
You're only checking for collisions on two axis, x and y. You need to use Pythagoras' theorem to detect on all axis at the cost of efficiency. For example.
Your algorithm will detect a collision around the point where these two balls are, since if you draw a tangent line along the x or y axis from one ball it goes through the other ball: http://jsfiddle.net/XpXzW/1/
Here you can see where they should actually collide:
http://jsfiddle.net/wdVmQ/1/
If you change your collision detection algorithm to check for perfect collisions (it will be less efficient) you can get rid of your buffer too:
http://jsfiddle.net/ucxER/
(Using Pythagoras' theorem the formula for a collision is:
Math.sqrt((this.x - balli.x)*(this.x - balli.x)
+ (this.y - balli.y)*(this.y - balli.y)) < 2*radius
Also what Jarrod commented is very smart. You can speed it up by using a technique like that. Since the square root is only calculated when the balls are close to each other:
http://jsfiddle.net/bKDXs/

Categories