Synchronize the animation timing with data (ThreeJS) - javascript

I have a data, Time in milliseconds and position(x,y,z) of object to be at that time.
msec |poz_x |poz_y |poz_z
------------------------------
0 |318 |24 |3
25 |318 |24 |3
49 |318 |23 |3
70 |318 |22 |2
91 |318 |22 |2
113 |318 |21 |1
136 |318 |21 |1
e.t.c
The problem is that the time difference between actual and next data vary (coming from sensor).
I'm looking for a way to do the animation in real time.
If in my data I have 60 second of information, it need to animate in browser during 60 second.
I have read that requestAnimationFrame( animate ); will repeat the function 60 time per second, but if my scene is heavy I imagine the frame rate will go down. In any case this can't solve my problem.
I'm looking for a robust solution that doesn't depend on the current framerate of the browser.
Please help.

There are a couple of ways to solve that, with and without libraries.
And you are right, it is not quite as simple as just counting the number of ticks of the animation-loop as there is no guarantee it happens every 1/60 second. But the animation-frame callback (loop in the code below) will get a timestamp passed as first parameter that can be used to calculate animation-progress.
So, in javascript, that could be something like this:
// these are your keyframes, in a format compatible with THREE.Vector3.
// Please note that the time `t` is expected in milliseconds here.
// (must have properties named x, y and z - otherwise the copy below doesn't work)
const keyframes = [
{t: 0, x: 318, y: 24, z: 3},
{t: 25, x: 318, y: 24, z: 3},
// ... and so on
];
// find a pair of keyframes [a, b] such that `a.t < t` and `b.t > t`.
// In other words, find the previous and next keyframe given the
// specific time `t`. If no previous or next keyframes is found, null
// is returned instead.
function findNearestKeyframes(t) {
let prevKeyframe = null;
for (let i = 0; i < keyframes.length; i++) {
if (keyframes[i].t > t) {
return [prevKeyframe, keyframes[i]];
}
prevKeyframe = keyframes[i];
}
return [prevKeyframe, null];
}
const tmpV3 = new THREE.Vector3();
function loop(t) {
const [prevKeyframe, nextKeyframe] = findNearestKeyframes(t);
// (...not handling cases where there is no prev or next here)
// compute the progress of time between the two keyframes
// (0 when t === prevKeyframe.t and 1 when t === nextKeyframe.t)
let progress = (t - prevKeyframe.t) / (nextKeyframe.t - prevKeyframe.t);
// copy position from previous keyframe, and interpolate towards the
// next keyframe linearly
tmpV3.copy(nextKeyframe);
someObject.position
.copy(prevKeyframe)
.lerp(tmpV3, progress);
// (...render scene)
requestAnimationFrame(loop);
}
// start the animation-loop
requestAnimationFrame(loop);
EDIT: To address one question from the comments about optimizing the findNearestKeyframes-function:
Once you get to several thousands of keyframes it might make sense to optimize that a bit, yes. For something like a few hundred it wouldn't be worth the effort (i'd categorize that as premature optimization).
To optimize, you could create an index-table in order to skip over irrelevant sections of the array. For instance, you could store the index in the keyframes-array for the start of every 10 seconds or something like that, that way - when you're searching for keyframes around t = 12.328s, you could start at a higher index based on precomputed information. There are probably a lot of other algorithms and structures that you could use to speed it up.

Related

How to kill bullet after a certain of distance?

How to kill bullet after a certain amount of time, or certain amount of distance ? I'm using JavaScript and Phaser 3.
if (keyA.isDown && time > lastFired || isDown && time > lastFired) {
var bullet = bullets.create(player.x , player.y, 'bullet');
bullet.setVelocity( -800, 0);
lastFired = time + 90;
}
If it should be after some time, you could do something like this.
(here some more details from the Docs: from the Docs )
But to be honest I don't know, if this would impact the performance, or not.
...
// the 500 ist the timeout in ms
this.time.delayedCall(500, () => bullet.destroy();
....
(add this line after creating the bullet)
Or you cout use a time event
this.time.addEvent({
delay: 500,
callback: ()=>{
bullet.destroy()
},
loop: false
});
for destroying by distance: depending if it is one bullet or many. you could add the bullet to a group and check the distance in the update function, and it its too far you detroy it/them.

How to smoothly increase a number to Y in X milliseconds exponentially

I'm making a semi-realistic 'physics' engine in node.js, if you can even call it that and I want to accelerate exponentially. E.g. from 0m/s to 4.5m/s in 2 seconds, then maybe decelerate to 0m/s in 3 seconds. Obviously for the deceleration part I can probably get away with inputting a negative number.
Here's a picture of what I'm thinking of, not sure if what I expect in the graph is the same thing as exponents.
I don't have any code, I thought I could base it off something like setInterval, but that would be linear.
You're right SetInterval can only provide with a fixed speed whereas what you need is dynamic speed.
One way is to make two arrays with an equal number of items. With first array named duration and second name speed. The variable Y will be changed by the speed for the duration corresponding to the # of the speed. See here :
var Y = 0; // The variable that is to be changed
var speed = [4.5, 0, -4.5]; // The acceleration in m/s
var time = [2, 4, 2]; // Duration corresponding to each acceleration in s
var sec = 1000; // 1 second = 1000 milliseconds
var i = 0; // A counter to remember the item number in the arrays
var dur = 0; // A variable to hold the duration for which a particular speed has currently lasted. This variable helps to validate the duration for which the Y is changed.
// The function that holds the logic
function tick() {
// Checking if duration is equal to or has crossed the user specified duration for the speed
if(dur >= time[i]*1000){
// If yes, move on to the next speed and duration
i++;
dur = 0;
if(i > speed.length-1){clearInterval(loop);} // If there are no more items in the array stop the interval
}
else {
// If not, continue increasing Y and dur
Y = Math.round(Y*1000 + (speed[i]/50)*1000)/1000 // A simple workaround to avoid the error in arthimetic calculation that occurs while using floats (decimal numbers) in JS.
console.log(Y); // This line is only for debugging purposes. IT CAN BE REMOVED
dur += sec/50;
}
}
var loop = setInterval(tick, sec/50);

Get a list of overlapping time ranges from a set of appointments

Title isn't great, and I'm open to suggestions.
Here's my basic problem:
I have a set of appointments, each with a start time and end time.
Given that set, what I want is a new set of ranges [ start_time, end_time ] for all periods where there are n overlapping appointments.
So, for example, given the set (timestamps simplified to small numbers for readability)
[
[ 1, 3 ],
[ 2, 4 ],
[ 2, 4 ],
[ 5, 7 ],
[ 6, 8 ],
[ 7, 8 ]
]
...and assuming I want all ranges that have at least 3 different appointments occurring within them, the result should be
[
[ 2, 3 ],
[ 6, 7 ]
]
To make this a bit less abstract...
Imagine I run a 24-hour window-tinting service with 3 installers on staff at all times. On my website, I want to show all available installation times. So I need to hide any time ranges where I've already got 3 appointments scheduled.
Not necessarily asking anyone to write code – but if there's a well-known algorithm for this class of problem that someone could point me to, I'd appreciate it.
Thanks.
[EDIT] added javascript tag because i'll be implementing this in Node, but answers don't need to be in JS.
[EDIT 2] I'm looking for a pretty general solution, so let's say that appointments can start at any time (not normalized to hour or 30 minute chunks) and can be of any duration
I think it works to create a histogram from the input ranges, then iterate through the histogram locating ranges where the height is greater than or equal to your target overlap, in this case 3.
BTW, I don't think [6,7] is a valid range given your inputs - I believe it should be [7,7]. At least that's what my code produces :)
Here's some Java code to illustrate:
public static void main(String[] args)
{
int[][] ranges = {{1,3},{2,4},{2,4},{5,7},{6,8},{7,8}};
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for(int[] range : ranges)
{
min = Math.min(min, range[0]);
max = Math.max(max, range[1]);
}
int[] hist = new int[1+max-min];
for(int[] range : ranges)
for(int i=range[0]; i<=range[1]; i++) hist[i-min]++;
int overlap = 3;
for(int i=0; i<hist.length; i++)
{
int j = i;
while(i<hist.length && hist[i] >= overlap) {i++;}
if(i>j)
System.out.println((j+min) + " : " + (i+min-1));
}
}
Output:
2 : 3
7 : 7
EDIT
I was dissatisfied with histogram approach since it relies on integer ranges and would be inefficient for long ranges. It occurred to me that you could instead sort the range endpoints, keeping track of whether they were at the start or end of the range, then walk through the endpoints keeping a counter of active ranges (increment when you encounter a start, decrement when you encounter an end). When the counter first rose above or fell below your threshold, in your case 3, you'd output the range.
I now see that MBo suggested this same approach.
Here's some more code to illustrate:
static class RangeEnd
{
int time;
int delta;
public RangeEnd(int pos, int delta)
{
this.time = pos;
this.delta = delta;
}
}
public static void main(String[] args)
{
int[][] ranges = {{ 1,3},{2,4},{2,4},{5,7},{6,8},{7,8}};
RangeEnd[] ends = new RangeEnd[2*ranges.length];
int i=0;
for(int[] range : ranges)
{
ends[i++] = new RangeEnd(range[0], 1);
ends[i++] = new RangeEnd(range[1], -1);
}
Arrays.sort(ends, new Comparator<RangeEnd>()
{
#Override
public int compare(RangeEnd e1, RangeEnd e2)
{
if(e1.time < e2.time) return -1;
else if(e1.time > e2.time) return 1;
else if (e1.delta > e2.delta) return -1;
else return 1;
}
});
int overlap = 3;
int count = 0;
boolean active = false;
int start = 0;
for(RangeEnd end : ends)
{
count += end.delta;
if(count >= overlap)
{
if(!active)
{
start = end.time;
active = true;
}
}
else if(active)
{
System.out.println(start + " : " + end.time);
active = false;
}
}
}
Make array/list of pairs: {time, flag = +1 for start of interval, -1 for the end of interval}
Sort list by time key. In case of tie account for start/end flag (end before start if intervals like [1,2] and [2,3] should not intersect)
Make Counter = 0
Traverse list, for every pair add flag to Counter. When Counter changes from n-1 to n - output range begins, when Counter changes from n to n-1 - output range ends
Assuming you can place things, say, on 30 minute intervals, then just start at first interval with #appts=0, and at every interval point, increment for every appt starting now and decrement for every appt ending now. #appts will always track how many appts are active at the current interval.
If you want to be really crazy efficient, you can "bucket sort" your starting end ending times into buckets for each interval, then the whole process will be linear. But unless you have a super large number of appointments, it will also work to just look them up as you go along.
What do your time stamps and ranges look like exactly? Are they daily/ hour / half hour / minute specific?
Here's a possible solution:
Let's say your time stamps are hour specific.
Declare a dictionary to hold a string key and int value. Key will represent time stamp to the hour e.g. "08-30-17 23". Value will be the count of how many appointments will be/are taking place in that hour.
Now Loop through your set of ranges. For each range, use another loop to go through the hours between the start and end time. For each of those hours, increment the count by 1 in the dictionary for that time stamp (with hour granularity).
At the end you should have the number of appointments taking place for every hour found in your data. If you have three appointments between 5 and 6pm and also 6 and 7pm of a given day then you'll need some more logic to transform that into a 5 to 7pm range.

Best d3 scale for mapping integers range

I want to construct a scale that maps a range of successive integers (indexes of characters in a string) to regular intervals in another range of integers (pixels, say 0-600). That is, I would like to assign characters to pixels and conversely as regularly as possible, the length of one not being necessarily a multiple of the other.
For instance, mapping [0,1,2,3] to 400 pixels, I would expect
0 -> 0-99
1 -> 100-199
2 -> 200-299
3 -> 300-399
and conversely
0-99 -> 0
100-199 -> 1
200-299 -> 2
300-399 -> 3
while for mapping 0-4000 to 400 pixels, I would expect
0-9 -> 0
10-19 -> 1
etc.
What is the best scale to use for this in d3 ?
On one hand I am afraid that discrete scales will not use the fact that the domain is equally separated and generate a huge switch statement if the number of elements is big. Since I will use the scale on every element to draw an image, I am worried about performance.
On the other hand, a linear scale such as
d3.scaleLinear()
.domain([0,399]) // 400 pixels
.rangeRound([0,3]) // 4 elements
gives me
0 0
66 0 // 1st interval has 66 pixels
67 1
199 1 // other intervals have 132 pixels
200 2
332 2
333 3 // last interval has 66 pixels
400 3
(fiddle)
so the interpolator returns unequal intervals (shorter at the ends).
Edit: not using d3, it is not hard to implement:
function coordinateFromSeqIndex(index, seqlength, canvasSize) {
return Math.floor(index * (canvasSize / seqlength));
}
function seqIndexFromCoordinate(px, seqlength, canvasSize) {
return Math.floor((seqlength/canvasSize) * px);
}
Too bad only if it does not come with d3 scales, since it would become much more readable.
The d3 Quantize Scale is the best option if you want to map onto an interval. The scale maps between discrete values and a continuous interval, though. I am not 100% clear on what you want to do, but let's look at how I could do a few of the things you mention with the quantize scale.
Mapping integers to intervals is straightforward, as long as you know that d3 uses half-open intervals [,) to break up the continuous domain.
var s1 = d3.scaleQuantize()
.domain([0,400])
.range([0,1,2,3]);
s1.invertExtent(0); // the array [0,100] represents the interval [0,100)
s1.invertExtent(1); // [100,200)
s1.invertExtent(2); // [200,300)
s1.invertExtent(3); // [300,400)
You could also enumerate the discrete values:
var interval = s.invertExtent(0);
d3.range(interval[0], interval[1]); // [0, 1, ... , 399]
These are nice values you've given though, and since you want to map integers to intervals of integers, we will need rounding when numbers aren't divisible. We can just use Math.round though.
var s2 = d3.scaleQuantize()
.domain([0,250])
.range([0,1,2,3]);
s2.invertExtent(0); // [0, 62.5)
s2.invertExtent(0).map(Math.round); // [0,63) ... have to still interpret as half open
There is no mapping from the interval itself to the integer, but the scale maps a point in an interval from the domain (which is continuous) to its value in the range.
[0, 99, 99.9999, 100, 199, 250, 399, 400].map(s1); // [0, 0, 0, 1, 1, 2, 3, 3]
I also suspect you switched the output of rangeRound from the linear scale with something else. I get
var srr = d3.scaleLinear()
.domain([0,3]) // 4 elements
.rangeRound([0,399]);
[0,1,2,3].map(srr); // [0, 133, 266, 399]
and
var srr2 = d3.scaleLinear()
.domain([0,4]) // 4 intervals created with 5 endpoints
.rangeRound([0,400]);
[0,1,2,3,4].map(srr2); // [0, 100, 200, 300, 400]
The output looks like a scale to us with a bar graph with 50% padding (then each position would be the midpoint of an interval that is 132 pixels). I am going to guess the cause is that rangeRound uses round to interpolate, rather than floor.
You could use a function designed for bar graphs also, if you want the width of the interval.
var sb = d3.scaleBand().padding(0).domain([0,1,2,3]).rangeRound([0,400]);
[0,1,2,3].map(sb); // [0, 100, 200, 300]
sb.bandwidth(); // 100
Not that any of this makes the code simpler.
Once I get to the functions you implement, it seems like the requirements are much simpler. There aren't any intervals involved really. The problem is that there isn't a one-to-one mapping. The best solution is either what you have done or to just use two linear scales with a custom interpolator (to find the floor, rather than rounding.
var interpolateFloor = function (a,b) {
return function (t) {
return Math.floor(a * (1 - t) + b * t);
};
}
var canvasSize = 400;
var sequenceLength = 4000;
var coordinateFromSequenceIndex = d3.scaleLinear()
.domain([0, sequenceLength])
.range([0, canvasSize])
.interpolate(interpolateFloor);
var seqIndexFromCoordinate = d3.scaleLinear()
.domain([0, canvasSize ])
.range([0, sequenceLength])
.interpolate(interpolateFloor);

jQuery easing function — variables' comprehension

How does the easing function for jQuery work? Take for example:
easeInQuad = function (x, t, b, c, d) {
return c*(t/=d)*t + b;
};
How does that work? What does each parameter hold? How would I implement some dumb easing for an animation?
Also how would I attach an easing pattern to jQuery, is loading it into $.easing good enough?
According to the jQuery 1.6.2 source, the meaning of the easing function is as follows. The function is called at various points in time during the animation. At the instants it is called,
x and t both say what the time is now, relative to the start of the animation. x is expressed as a floating point number in the range [0,1], where 0 is the start and 1 is the end. t is expressed in milliseconds since the start of the animation.
d is the duration of the animation, as specified in the animate call, in milliseconds.
b=0 and c=1.
The easing function should return a floating point number in the range [0,1], call it `r`. jQuery then computes `x=start+r*(end-start)`, where `start` and `end` are the start and end values of the property as specified in the call to animate, and it sets the property value to `x`.
As far as I can see, jQuery doesn't give you direct control over when the animation step function is called, it only lets you say "if I am called at time t, then I should be thus far through the entire animation." Therefore you cannot, for example, ask for your object to be redrawn more frequently at times when it is moving faster. Also, I don't know why other people say b is the start value and c is the change -- that's not what jQuery source code says.
If you wanted to define your own easing function to do the same as easeInQuad, for example,
$.extend(jQuery.easing,{myfunc:function(x,t,b,c,d) { return x*x; }})
$('#marker').animate({left:'800px'},'slow','myfunc');
$.extend(jQuery.easing,{myfunc:function(x,t,b,c,d) { return x*x; }})
$('#marker').animate({left:'500px'},'slow','myfunc');
#marker { position: absolute; left: 10px; top: 50px; background: red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='marker'>Hello World!</div>
A concrete example,
Suppose our initial value is 1000 and we want to reach 400 in 3s:
var initialValue = 1000,
destinationValue = 400,
amountOfChange = destinationValue - initialValue, // = -600
duration = 3,
elapsed;
Let's go from 0s to 3s:
elapsed = 0
$.easing.easeInQuad(null, elapsed, initialValue, amountOfChange, duration)
//> 1000
elapsed = 1
$.easing.easeInQuad(null, elapsed, initialValue, amountOfChange, duration)
//> 933.3333333333334
elapsed = 2
$.easing.easeInQuad(null, elapsed, initialValue, amountOfChange, duration)
//> 733.3333333333334
elapsed = 3
$.easing.easeInQuad(null, elapsed, initialValue, amountOfChange, duration)
//> 400
So compared to the synopsis:
$.easing.easeInQuad = function (x, t, b, c, d) {...}
we can deduce:
x t b c d
| | | | |
null elapsed initialValue amountOfChange duration
NB1: One important thing is that elapsed(t) and duration(d) should be expressed in the same unit, eg: here 'seconds' for us, but could be 'ms' or whatever. This is also true for initialValue(b) and amountOfChange(c), so to sum-up:
x t b c d
| | | | |
null elapsed initialValue amountOfChange duration
^ ^ ^ ^
+----------|----=unit=----|------------+
+----=unit=----+
NB2: Like #DamonJW, I don't know why x is here. It does not appear in Penner's equations and does not seem to be used in result: let always set him to null
t: current time, b: start value, c: change from the start value to the end value, d: duration.
Here is how it works: http://james.padolsey.com/demos/jquery/easing/ (curve representing when a CSS property is changed).
Here is how I would implement some dumb easing: http://www.timotheegroleau.com/Flash/experiments/easing_function_generator.htm (math is not my strong suit)
You would extend like any of these: http://code.google.com/p/vanitytools/source/browse/trunk/website/js/custom_easing.js?spec=svn29&r=29 - good enough!
This plugin implements the most common easing functions: http://gsgd.co.uk/sandbox/jquery/easing/
Looked through the 1.11 jquery code. The x parameter seems to mean 'percent', independent from initial time value.
So, x is always (0 <= x <= 1) (means abstract coefficient), and t is x * d (means time elapsed).

Categories