Page 1 of 1

Projectile Animation too slow

Posted: Fri Dec 22, 2006 6:29 pm
by Ambush Commander
I am currently using this code to animate a projectile flying through the air. The animation takes a long time to draw, however. Are there any obvious inefficiencies in this code, or do I just have to say, "That's the way it is?" It's not too slow, but could be zippier.

Code: Select all

// require_once 'classes/Math.js';

ProjectileLab = {
    
    Version: '1.0.0',
    Author: 'Edward Z. Yang',
    
    plot_interval: .0125,
    animate_interval: 20,
    
    plotProjectile: function (speed, angle, height, range) {
        
        var interval = this.plot_interval;
        
        if (speed == 0 && (height == 0 || angle == 0)) return Array(Array(0,0));
        if (height == 0 && angle == 0) return Array(Array(0,0));
        
        var rad = angle * Math.RpD;
        var speed_x = speed * Math.cos(rad);
        var speed_y = speed * Math.sin(rad); // will change
        var d_x = speed_x * interval;
        var d_speed_y = -4.9 * interval;
        var time = range / speed_x;
        
        var x = 0;
        var y = height;
        var points = Array();
        
        for (i = 0; y >= 0; i += interval) {
            x += d_x;
            y += speed_y * interval;
            speed_y += d_speed_y;
            points.push(Array(x, y));
        }
        return points;
    },
    
    animateProjectile: function (id, points) {
        
        var px_per_meter = 5;
        
        var area = $(id);
        var dots = Array();
        for (i = 0; i < points.length; i++) {
            var dot = document.createElement('div');
            dot.className = 'dot';
            dot.style.left = points[i][0] * px_per_meter + "px";
            dot.style.bottom = points[i][1] * px_per_meter + "px";
            dot.style.backgroundColor = '#FFF';
            area.appendChild(dot);
            dots.push(dot);
        }
        
        var frame = 0;
        var callback = function () {
            dots[frame++].style.backgroundColor = '#000';
            if (frame >= dots.length) {
                clearInterval(interval_id1);
                //clearInterval(interval_id2);
            }
        };
        var interval_id1 = setInterval(callback, this.animate_interval);
        //var interval_id2 = setInterval(callback, this.animate_interval);
        
    },
    
}
[/syntax]

Posted: Sat Dec 23, 2006 6:17 am
by Ollie Saunders
I'm not sure. I did try and run it though. Like this:

Code: Select all

<html>
<head>
<title>ProjectileTest</title>
<script type="text/javascript">
ProjectileLab = {
   
    Version: '1.0.0',
    Author: 'Edward Z. Yang',
   
    plot_interval: .0125,
    animate_interval: 20,
   
    plotProjectile: function (speed, angle, height, range) {
       
        var interval = this.plot_interval;
       
        if (speed == 0 && (height == 0 || angle == 0)) return Array(Array(0,0));
        if (height == 0 && angle == 0) return Array(Array(0,0));
       
        var rad = angle * Math.RpD;
        var speed_x = speed * Math.cos(rad);
        var speed_y = speed * Math.sin(rad); // will change
        var d_x = speed_x * interval;
        var d_speed_y = -4.9 * interval;
        var time = range / speed_x;
       
        var x = 0;
        var y = height;
        var points = Array();
       
        for (i = 0; y >= 0; i += interval) {
            x += d_x;
            y += speed_y * interval;
            speed_y += d_speed_y;
            points.push(Array(x, y));
        }
        return points;
    },
   
    animateProjectile: function (id, points) {
       
        var px_per_meter = 5;
       
        var area = document.getElementById(id);
        var dots = Array();
        for (i = 0; i < points.length; i++) {
            var dot = document.createElement('div');
            dot.className = 'dot';
            dot.style.left = points[i][0] * px_per_meter + "px";
            dot.style.bottom = points[i][1] * px_per_meter + "px";
            dot.style.backgroundColor = '#FFF';
            area.appendChild(dot);
            dots.push(dot);
        }
       
        var frame = 0;
        var callback = function () {
            dots[frame++].style.backgroundColor = '#000';
            if (frame >= dots.length) {
                clearInterval(interval_id1);
                //clearInterval(interval_id2);
            }
        };
        var interval_id1 = setInterval(callback, this.animate_interval);
        //var interval_id2 = setInterval(callback, this.animate_interval);
       
    },
   
} 
window.onload = function() {
    ProjectileLab.animateProjectile('foo', ProjectileLab.plotProjectile(100, 100, 100, 100));
}
//</script>
</head>
<body>
<h1 id="foo" style="position:absolute; top:100px; left:100px;">demo</h1>
</body>
</html>
and nothing happened.

Posted: Sat Dec 23, 2006 10:40 am
by Ambush Commander
That's because you're missing a bunch of external libraries. Try this: http://www.thewritingpot.com/projectile ... /plot.html

Posted: Sat Dec 23, 2006 11:15 am
by Ollie Saunders
That's because you're missing a bunch of external libraries
Ahh well yes, that can be a problem.
I enjoyed your smoke test :).

Couple of ideas
  • reduce animate_interval: 20,
  • write what you need yourself instead of using libraries
  • minimise the number of things you do with the DOM as they tend to be expensive
  • shorten object chains through assignments
I'm not sure how much effect those will have in general putting pixels in JS is always going to be slow.

Posted: Sat Dec 23, 2006 11:22 am
by Ambush Commander
Yes, it's a very fun smoketest. I got a little carried away, I admit, but the things I did there will be helpful when I start coding the real application.
reduce animate_interval: 20,
Setting it to zero doesn't help much, so I figured it would me more user-friendly to set it at a reasonable value.
write what you need yourself instead of using libraries
The callback uses no libraries at all, so that's a non-issue. Besides, I like libraries, they save a lot of time and effort.
minimise the number of things you do with the DOM as they tend to be expensive
Hmm... does twiddling around with CSS attributes count as messing around with the DOM? You'll notice that before the animation happens, all of the points are plotted with visibility:hidden, and then they're switched on for the animation. That sped it up a little but not an amazing amount.
shorten object chains through assignments
Not sure what you mean.

Currently, in Firefox and Opera I can make it arbitrarily fast by increasing the amount of interval calls (it's more choppy though), but this seems to have no effect in Internet Explorer. I'm also wondering whether or not grouping the pixels together and turning on 4 or 5 at a time would help.

Posted: Sat Dec 23, 2006 11:28 am
by Ollie Saunders
It sounds like you've done all the right things. I'm surprised just changing the CSS is so slow.
Not sure what you mean.
It's a really tiny thing. But this is what I'm getting at

Code: Select all

dot.style.left = points[i][0] * px_per_meter + "px";
dot.style.bottom = points[i][1] * px_per_meter + "px";
dot.style.backgroundColor = '#FFF'; 
becomes

Code: Select all

var style = dot.style;
style.left = points[i][0] * px_per_meter + "px";
style.bottom = points[i][1] * px_per_meter + "px";
style.backgroundColor = '#FFF'; 
Setting it to zero doesn't help much, so I figured it would me more user-friendly to set it at a reasonable value.
Yeah you are probably right.

Posted: Sat Dec 23, 2006 11:29 am
by Ambush Commander
You know what, while we're at it, I'm going to field another question.

The calculations for the points necessary are a bit resource intensive, and it's not uncommon to see my CPU usage spike up when I ask the application to plot a few points. This is no problem with some of the smaller parabolas, but when I get to big speeds and big angles, the application literally "hangs" for a few seconds, before drawing the trajectory. This is detrimental to the liveness of the application.

I've tried various techniques to give feedback to the user through throbber images and the like, but when the browser hangs, the animated gif disappears! I've even tried plaintext, but it doesn't seem to show up either. Any comments?

Edit - Didn't see your reply.
It's a really tiny thing.
Improves readability a bit though, I'll try it.
I'm surprised just changing the CSS is so slow.
I am too. But there are a lot of div dots to switch on.

Posted: Sat Dec 23, 2006 11:31 am
by Ollie Saunders
If you are doing something like unhiding a layer saying "please wait" before you do the calculations then I really can't understand why it wouldn't appear. a bug?

Edit: new stuff:
I am too. But there are a lot of div dots to switch on.
Only switch on the even ones?