2016年8月18日木曜日

開発環境

Pythonからはじめる数学入門 (Amit Saha (著)、黒川 利明 (翻訳)、オライリージャパン)の2章(データをグラフで可視化する)、2.6(プログラミングチャレンジ)、問題2-3(投射軌跡比較プログラムの拡張)をJavaScript(とD3.js)で取り組んでみる。(バージョンアップで D3.js にいろいろ変更点があったみたい。)

問題2-3(投射軌跡比較プログラムの拡張)

コード(Emacs)

HTML5

<div id="graph0"></div>
<div id="output0"></div>
<label forid="tranjectories0">How many tranjectories?</label>
<input id="tranjectories0" type="number" min="1" step="1" value="3">
<div id="velocities0"></div>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"></script>

<script src="sample3.js"></script>

JavaScript

(function () {
    'use strict';
    var g = 9.8,
        input_tranjectories = document.querySelector('#tranjectories0'),
        div_velocities = document.querySelector('#velocities0'),
        div_output = document.querySelector('#output0'),
        div_graph = document.querySelector('#graph0'),
        class_velocities,
        width = 600,
        height = 400,
        padding = 50,
        frange,
        toRadian,
        getTranjectory,
        getTranjectories,
        appendVelocities,
        drawGraph;

    frange = function (start, end, step) {
        var numbers = [],
            i;

        for (i = start; i < end; i += step) {
            numbers.push(i);
        }
        return numbers;
    };
    toRadian = function (angle) {
        return angle * Math.PI / 180;
    };
    getTranjectory = function (u, theta, t_flight) {
        var intervals = frange(0, t_flight, 0.05),
            xs,
            ys,
            xys = [],
            i,
            max;

        xs = intervals.map(function (t) {
            return u * Math.cos(theta) * t;
        });
        ys = intervals.map(function (t) {
            return u * Math.sin(theta) * t - 0.5 * g * t * t;
        });
        for (i = 0, max = xs.length; i < max; i += 1) {
            xys.push([xs[i], ys[i]]);
        }
        return xys;
    };
    getTranjectories = function () {
        var tranjectories = parseInt(input_tranjectories.value, 10);

        appendVelocities(tranjectories);
    };
    appendVelocities = function (tranjectories) {
        var html = '',
            i,
            max;

        div_velocities.innerHTML = '';
        for (i = 1; i <= tranjectories; i += 1) {
            html +=
                'Enter the initial velocity ' + i + ' (m/s): ' +
                '<input class="velocities" id="velocity' + i +
                '" type="number" ' +
                'min="1"step="1" value="' +
                (Math.floor(Math.random() * 70) +20) +
                '"><br>' +
                'Enter the angle of projection ' + i + ' (degrees): ' +
                '<input class="velocities" velocities id="projection' + i +
                '" type="number" min="1" step="1" value="' +
                (Math.floor(Math.random() * 70) + 20) + '"><br>';
        }
        div_velocities.innerHTML = html;    
        class_velocities = document.querySelectorAll('.velocities');
        
        drawGraph();
        
        for (i = 0, max = class_velocities.length; i < max; i += 1) {
            class_velocities[i].onchange = drawGraph;
            class_velocities[i].onkeyup = drawGraph;
        }
    };
    drawGraph = function () {
        var velocities = [],
            angles = [],
            velocity,
            angle,
            radian,        
            t_flight,
            t,
            x,
            y,
            xys = [],
            plot = [],
            plot_len,
            indexes = [],
            index = 0,
            i,
            max,
            xmax,
            ymax,
            color,
            colors = [],
            svg,
            xscale,
            yscale,
            xaxis,
            yaxis;

        for (i = 0, max = class_velocities.length; i < max; i += 1) {
            if (i % 2 === 0) {
                velocities.push(parseInt(class_velocities[i].value, 10));
            } else {
                angles.push(parseInt(class_velocities[i].value, 10));
            }
        }
        
        div_output.innerHTML = '';
        for (i = 0, max = velocities.length; i < max; i += 1) {        
            velocity = velocities[i];
            angle = angles[i];
            radian = toRadian(angle);
            t_flight = 2 * velocity * Math.sin(radian) / g;
            x = velocity * Math.cos(radian) * t_flight;
            t = t_flight / 2;
            y = velocity * Math.sin(radian) * t - 0.5 * g * t * t;
            color = 'rgb(' +
                Math.floor(Math.random() * 200) + ', ' +
                Math.floor(Math.random() * 200) + ', ' +
                Math.floor(Math.random() * 200) + ')';
            colors.push(color);
            div_output.innerHTML +=
                '<span style="color:' + color + ';">----------</span><br>' +
                'velocity: ' + velocity + ', angle: ' + angle + '<br>' +
                '    time: ' + t_flight + 
                ', x: ' + x + ', y: ' + y + '<br>';
            xys = getTranjectory(velocity, radian, t_flight);
            plot = plot.concat(xys);
            index += xys.length;
            indexes.push(index - 1);
        }
        plot_len = plot.length;
        indexes.push(plot_len - 1);

        xmax = d3.max(plot.map(function (elem) {
            return elem[0];
        }));
        xscale = d3.scaleLinear()
            .domain([0, xmax])
            .range([padding, width - padding]);
        xaxis = d3.axisBottom().scale(xscale);

        ymax = d3.max(plot.map(function (elem) {
            return elem[1];
        }));
        yscale = d3.scaleLinear()
            .domain([0, ymax])
            .range([height - padding, padding]);

        yaxis = d3.axisLeft().scale(yscale);

        div_graph.innerHTML = '';
        svg = d3.select('#graph0')
            .append('svg')
            .attr('width', width)
            .attr('height', height);
        
        color = colors.shift();
        svg.selectAll('line')
            .data(plot)
            .enter()
            .append('line')
            .attr('x1', function (d, i) {
                if (indexes.indexOf(i) !== -1) {
                    return xscale(0);
                }
                return xscale(d[0]);
            })
            .attr('y1', function (d, i) {
                if (indexes.indexOf(i) !== -1) {
                    return yscale(0);
                }
                return yscale(d[1]);
            })
            .attr('x2', function (d, i) {
                if (indexes.indexOf(i) !== -1) {
                    return xscale(0);
                }
                return xscale(plot[i+1][0]);
            })
            .attr('y2', function (d, i) {
                if (indexes.indexOf(i) !== -1) {
                    return yscale(0);
                }
                return yscale(plot[i+1][1]);
            })
            .attr('stroke', function (d, i) {
                if (indexes.indexOf(i) !== -1) {
                    color = colors.shift();
                    return 'white';
                }
                return color;
            });

        svg.append('g')
            .attr('transform', 'translate(0, ' + (height - padding) + ')')
            .call(xaxis);

        svg.append('g')
            .attr('transform', 'translate(' + padding + ', 0)')
            .call(yaxis);

        svg.append('text')
            .attr('x', xscale(xmax / 2))
            .attr('y', padding / 2)
            .attr('text-anchor', 'middle')
            .text('Projectile motion of a ball');
        
        svg.append('text')
            .attr('x', xscale(xmax / 2))
            .attr('y', height - padding / 4)
            .attr('text-anchor', 'middle')
            .text('x');

        svg.append('text')
            .attr('x', padding / 5)
            .attr('y', (height - padding) / 2 )
            .attr('text-anchor', 'middle')
            .text('y');
    };
    getTranjectories();
    input_tranjectories.onchange = getTranjectories;
    input_tranjectories.onkeyup = getTranjectories;
}());

0 コメント:

コメントを投稿