I need a custom easing function that takes breakpoints as parameters and eases between those breakpoints similar to this but this doesn't work:
function makeEasing(breakpoints) {
return t => {
const iPoint = Math.floor(t / breakpoints.length),
iDest = iPoint + 1;
return (breakpoints[iPoint] - breakpoints[iDest]) * (t);
};
}
Usage:
const easing = makeEasing([0.5, 0.0, 0.5, 1.0, 0.5]);
easing(0) // 0.5
// easing(0 + 1/4 * 0.5) // 0.25
easing(1/4) // 0.0
easing(2/4) // 0.5
easing(3/4) // 1.0
// easing(3/4 + 1/4 * 0.5) // 0.75
easing(4/4) // 0.5
let's say we can use this elastic easing function
const easing = t => {
return .04 * t / (--t) * Math.sin(25 * t);
};
i have two variables in the range [0.0, 1.0]
impact and targetImpact
I want impact to reach targetImpact slowly so I do:
// delta is passed time
impact += (targetImpact - impact) * delta * 0.001;
This works for interpolating between impact and targetImpact.
But I want elastic effect when impact reaches targetImpact, that is
impact goes up to targetImpact + 0.2,
then goes down to targetImpact - 0.2,
then goes up to targetImpact + 0.1,
then goes down to targetImpact - 0.1,
finally reaches targetImpact.
I think the fastest way for you to have this effect is to create 4 impacts in a row with your different targetImpact. Use the previous targetImpact as impact for next one.
Edit:
var easing = 0;
function makeEasing(breakpoints) {
breakpoints.forEach(t => {
let iPoint = Math.floor(t / breakpoints.length),
iDest = iPoint + 1;
easing = (breakpoints[iPoint] - breakpoints[iDest]) * (t);
})
}
makeEasing([0.5, 0.0, 0.5, 1.0, 0.5]);
Thanks #AppyGG for giving the idea this now works:
function makeEasing(breakpoints) {
const spaces = breakpoints.length - 1;
return t => {
const iPoint = Math.floor(t / (1 / spaces)),
iDest = iPoint + 1;
return breakpoints[iPoint] + (breakpoints[iDest] - breakpoints[iPoint]) * smoothstep(0, 1/spaces, (t % (1/spaces)));
};
}
function smoothstep (min, max, value) {
var x = Math.max(0, Math.min(1, (value-min)/(max-min)));
return x*x*(3 - 2*x);
};
function round(v, d = 100) {
return Math.round(v * d) / d;
}
function testEasing(easing) {
for (let i = 0; i< 1; i+= 0.01) {
console.log(round(i), round(easing(i)));
}
}
const easing2 = makeEasing([0.5, 0.0, 0.5, 1.0, 0.5]);
testEasing(easing2);
Related
I'm working on a predator-prey model for a dynamical systems book. I start by creating a dragable point for the initial condition. If I set up a second point whose coordinates are functions of the first point, I can drag one and it moves the other. I'm trying to get a 100 point orbit of the system, and I'm having difficulty. Here's a fiddle that works for a single point - https://jsfiddle.net/jford1906/gx86vbtc/21/
var board = JXG.JSXGraph.initBoard('jxgbox', {
boundingbox: [-0.1, 3, 1, -0.1],
axis: true,
grid: true,
showFullscreen: true
});
var p1 = board.create('point', [0.5, 0.5], {
name: 'A'
});
var coords = board.create('text',
[0.3, 2.8, function() {
return "Initial Condition: (" + JXG.toFixed(p1.X(), 2) + "," + JXG.toFixed(p1.Y(), 2) + ")";
}]
);
var p2 = board.create('point', [function() {
return 2 * p1.X() * (1 - p1.X()) - 0.5 * p1.X() * p1.Y()
}, function() {
return 4 * p1.Y() / 5 + 1.5 * p1.X() * p1.Y()
}], {
withLabel: false,
color: "blue",
opacity: 1,
size: 3
});
What I've tried so far is to plug each point of the orbit into an array, and have the next point run the same functions for it's coordinates as I did in the working example. It initially shows the whole orbit, but when I move the initial condition, all points except the last one in the orbit vanish. This fiddle shows how I've tried to do it - https://jsfiddle.net/jford1906/jra9g2d3/3/
var i; //indexing variable
var pts = [p1] //Put the initial condition in an array
for (i = 1; i < 100; i++) {
var p2 = board.create('point', [function() {
return 2 * pts[i - 1].X() * (1 - pts[i - 1].X()) - 0.5 * pts[i - 1].X() * pts[i - 1].Y()
}, function() {
return 4 * pts[i - 1].Y() / 5 + 1.5 * pts[i - 1].X() * pts[i - 1].Y()
}], {
withLabel: false,
color: "blue",
opacity: 1,
size: 1
});
pts.push(p2);
}
Ideas on why this might happen, or thoughts on a different approach? I've also tried putting the whole loop in a function and having that trigger when the point is dragged, but the same issue occurs.
This is a problem with JavaScript closures. It has been answered in https://groups.google.com/g/jsxgraph/c/Y1y1Mbd23ZQ. A very quick fix would be to define the variable i with let instead of var:
var pts = [p1] //Put the initial condition in an array
for (let i = 1; i < 100; i++) {
var p2 = board.create('point', [function() {
return 2 * pts[i - 1].X() * (1 - pts[i - 1].X()) - 0.5 * pts[i - 1].X() * pts[i - 1].Y()
}, function() {
return 4 * pts[i - 1].Y() / 5 + 1.5 * pts[i - 1].X() * pts[i - 1].Y()
}], {
withLabel: false,
color: "blue",
opacity: 1,
size: 1
});
pts.push(p2);
}
I have made a CSS-animation with Velocity.js:
https://codepen.io/blaustern_fotografie/pen/GvxWoW
The code is designed such that the circles are not allowed to go beyond the window on the left and right side. The function "my_animate" is responsible for this:
function my_animate(circle) {
var new_y = Math.floor(
Math.random() * ((height -50)-50)+50
);
var new_x = Math.floor(
Math.random() * ((width-50)-50)+50
);
var r = Math.random();
//var nd = Math.floor(r * 500 - 250);
$(circle).velocity(
{
translateX: new_x-$(circle).position().left,
translateY: new_y-$(circle).position().top,
//translateZ: nd,
opacity: r,
blur: Math.round((1 - r) * 5)
},
{
duration: Math.round(Math.random() * 10000 + 10000),
complete: function() {
my_animate(circle);
}
}
);
}
Does anyone know why the circles are passing the edges?
Problem seems here in calculating the new position.
The use of Math.random() is leading to random values being generated for new_x.
var new_x = Math.floor(
Math.random() * ((width -50)-50)+50
);
Then new_x-$(circle).position().left is leading to values being generated which are out of window range.
You should normalise your randomiser to generate new position such that the values generated are always between the min and max points on the viewport.
You need to make the Math.random in range of your window.width, otherwise the randomizer will just calculate values out of your window.width.
I wrote a function that should return a point on a bicubic Bézier surface from a 4x4 matrix of control points for parameters u and v, which are element of [0, 1], using Bernstein polynomials. But either my function doesn't work like it should, or my understanding of the matter is even worse than I thought.
The function to calculate the point looks like this:
var bezierSurface = function (u, v, p) {
var result = [];
var p00 = p[0], p01 = p[1], p02 = p[2], p03 = p[3],
p10 = p[4], p11 = p[5], p12 = p[6], p13 = p[7],
p20 = p[8], p21 = p[9], p22 = p[10], p23 = p[11],
p30 = p[12], p31 = p[13], p32 = p[14], p33 = p[15];
var uin = (1 - u),
vin = (1 - v);
var bu0 = Math.pow(uin, 3),
bu1 = 3 * u * Math.pow(uin, 2),
bu2 = 3 * Math.pow(u, 2) * uin,
bu3 = Math.pow(u, 3);
var bv0 = Math.pow(vin, 3),
bv1 = 3 * v * Math.pow(vin, 2),
bv2 = 3 * Math.pow(v, 2) * vin,
bv3 = Math.pow(v, 3);
for (var i = 0; i < 3; i++) {
result.push(
p00[i] * bu0 * bv0 +
p01[i] * bu0 * bv1 +
p02[i] * bu0 * bv2 +
p03[i] * bu0 * bv3 +
p10[i] * bu1 * bv0 +
p11[i] * bu1 * bv1 +
p12[i] * bu1 * bv2 +
p13[i] * bu1 * bv3 +
p20[i] * bu2 * bv0 +
p21[i] * bu2 * bv1 +
p22[i] * bu2 * bv2 +
p23[i] * bu2 * bv3 +
p30[i] * bu3 * bv0 +
p31[i] * bu3 * bv1 +
p32[i] * bu3 * bv2 +
p33[i] * bu3 * bv3
);
}
return result;
};
Most probably this is not the most efficient way to get the job done, but since I'm just getting started with parametric surfaces, I'm trying to keep things as simple as possible, yet not even thinking about tesselating the surface to get vertices for triangles or something like that.
Now, the problem appeared when I called the function with the following arguments:
var getSurfacePoint = function () {
var u = 0.5,
v = 0.25;
var cp = [
[-1.0, 0.0, -1.0],
[-0.5, 0.3, -0.8],
[ 0.5, 0.3, -0.8],
[ 1.0, 0.0, -1.0],
[-0.8, 0.3, -0.5],
[-0.3, 1.0, -0.4],
[ 0.3, 1.0, -0.4],
[ 0.8, 0.3, -0.5],
[-0.8, 0.3, 0.5],
[-0.3, 1.0, 0.4],
[ 0.3, 1.0, 0.4],
[ 0.8, 0.3, 0.5],
[-1.0, 0.0, 1.0],
[-0.5, 0.3, 0.8],
[ 0.5, 0.3, 0.8],
[ 1.0, 0.0, 1.0]
];
return bezierSurface(u, v, cp);
};
The result of calling bezierSurface via getSurfacePoint is -0.4437500000000001 for x, 0.5625 for y and -4.683753385137379e-17 for z, and that is not what I expected. I mean, at first sight, the return values for x and y seem plausible, but considering the values provided by the matrix of control points, the return value for z just looks completely wrong.
As far as I understand it, the points of a Bézier curve as well as the points of a Bézier surface are always enclosed within the convex hull of the control polygon, that is here represented by the points of the 4x4 matrix. So, when the range of z-values of the control points only goes from -1.0 to 1.0, how can the calculated point of the surface have a z-value < -4.0?
If we suppose the result is wrong, there must be something wrong with my function to calculate the point on the surface, but though alternately staring at bezierSurface and the mathematical definition of the Bézier surface for some time, I wasn't able to spot the error yet. I hope someone else can.
the return value for z just looks completely wrong
-4.683753385137379e-17, the value is (almost) 0. The result looks pretty right.
I'd like to use deceleration on a animation I'm running through request animation frame! I know how to do velocity, for deceleration I found this project https://github.com/gre/bezier-easing. I'm now TIAS, but not sure what to do https://github.com/gre/bezier-easing. I expect to see a decrease on speed at the end velocity <= parseFloat(attrs.radialBarPercentage). Code example:
var easing = BezierEasing(0, 0, 1, 0.5);
(function loop() {
velocity += (i + velocity) * friction;
// attempts:
//velocity = i - easing(i / 100);
//velocity = (i + velocity) * easing(i / 100);
if (velocity <= parseFloat(attrs.radialBarPercentage)) {
$knob.val(velocity).trigger('change');
i++;
animationFrame.request(loop);
}
})();
I found the solution, working for me:
// http://cubic-bezier.com/#0.25,0.25,0,1
var easing = BezierEasing(0.25, 0.25, 0, 0.9),
i = 0,
stepIncrementAmount = 0.25;
(function loop() {
// sorry about the * 100 but that's what $knob expects, scale range 0 > 100, and easing needs 0 to 1
velocity = easing(i / 100) * 100;
if (velocity <= parseFloat(attrs.radialBarPercentage)) {
$knob.val(velocity).trigger('change');
i += stepIncrementAmount;
animationFrame.request(loop);
}
})();
Can someone give me an idea how can i round off a number to the nearest 0.5.
I have to scale elements in a web page according to screen resolution and for that i can only assign font size in pts to 1, 1.5 or 2 and onwards etc.
If i round off it rounds either to 1 decimal place or none.
How can i accomplish this job?
Write your own function that multiplies by 2, rounds, then divides by 2, e.g.
function roundHalf(num) {
return Math.round(num*2)/2;
}
Here's a more generic solution that may be useful to you:
function round(value, step) {
step || (step = 1.0);
var inv = 1.0 / step;
return Math.round(value * inv) / inv;
}
round(2.74, 0.1) = 2.7
round(2.74, 0.25) = 2.75
round(2.74, 0.5) = 2.5
round(2.74, 1.0) = 3.0
Just a stripped down version of all the above answers:
Math.round(valueToRound / 0.5) * 0.5;
Generic:
Math.round(valueToRound / step) * step;
To extend the top answer by newtron for rounding on more than only 0.5
function roundByNum(num, rounder) {
var multiplier = 1/(rounder||0.5);
return Math.round(num*multiplier)/multiplier;
}
console.log(roundByNum(74.67)); //expected output 74.5
console.log(roundByNum(74.67, 0.25)); //expected output 74.75
console.log(roundByNum(74.67, 4)); //expected output 76
Math.round(-0.5) returns 0, but it should be -1 according to the math rules.
More info: Math.round()
and Number.prototype.toFixed()
function round(number) {
var value = (number * 2).toFixed() / 2;
return value;
}
var f = 2.6;
var v = Math.floor(f) + ( Math.round( (f - Math.floor(f)) ) ? 0.5 : 0.0 );
function roundToTheHalfDollar(inputValue){
var percentile = Math.round((Math.round(inputValue*Math.pow(10,2))/Math.pow(10,2)-parseFloat(Math.trunc(inputValue)))*100)
var outputValue = (0.5 * (percentile >= 25 ? 1 : 0)) + (0.5 * (percentile >= 75 ? 1 : 0))
return Math.trunc(inputValue) + outputValue
}
I wrote this before seeing Tunaki's better response ;)
These answers weren't useful for me, I wanted to always round to a half (so that drawing with svg or canvas is sharp).
This rounds to the closest .5 (with a bias to go higher if in the middle)
function sharpen(num) {
const rem = num % 1
if (rem < 0.5) {
return Math.ceil(num / 0.5) * 0.5 + 0.5
} else {
return Math.floor(num / 0.5) * 0.5
}
}
console.log(sharpen(1)) // 1.5
console.log(sharpen(1.9)) // 1.5
console.log(sharpen(2)) // 2.5
console.log(sharpen(2.5)) // 2.5
console.log(sharpen(2.6)) // 2.5
The highest voted answer above fails for:
roundHalf(0.6) => returns 0.5
roundHalf(15.27) => returns 15.5
The fixed one is as follows:
const roundHalf = (num) => {
return Math.floor(Math.ceil(num * 2) / 2)
}
As a bit more flexible variation of the good answer above.
function roundNumber(value, step = 1.0, type = 'round') {
step || (step = 1.0);
const inv = 1.0 / step;
const mathFunc = 'ceil' === type ? Math.ceil : ('floor' === type ? Math.floor : Math.round);
return mathFunc(value * inv) / inv;
}