Tangent to a graph

by dm89

HTML

<body>
    <figure id="graph"></figure>

CSS

#line path {
    stroke: black;
    fill: none;
}
#line path.overlay {
    stroke: white;
    stroke-opacity: 0;
    stroke-width: 10;
    fill: none;
}
#tangent {
    stroke: red;
    stroke-width: 2;
}
}

JavaScript

// compute data
function f(x) {
    return x * x;
};

var X_MIN = -5,
    X_MAX = 5,
    data = [];

for (var x = X_MIN; x <= X_MAX; x = x + 0.01) {
    data.push({
        x: x,
        y: f(x)
    });
};

// set up graph environment
var WIDTH = 500,
    HEIGHT = 350;

var svg = d3.select('#graph')
    .append('svg')
    .attr('width', WIDTH)
    .attr('height', HEIGHT);

// the x and y scales

var x_scale = d3.scale.linear()
    .range([10, WIDTH - 10])
    .domain([X_MIN, X_MAX])

var y_scale = d3.scale.linear()
    .range([HEIGHT - 10, 10])
    .domain([0, f(X_MAX)]);

// make a graph line

var line = d3.svg.line()
    .x(function (d) {
    return x_scale(d.x);
})
    .y(function (d) {
    return y_scale(d.y);
})
    .interpolate('cardinal')
    .tension(0);

// draw it

var g = svg.append('g')
    .attr('id', 'line')
g.append('path')
    .attr('d', line(data));

// make a transparant overlay to capture mousemovements

g.append('path')
    .classed('overlay', true)
    .attr('d', line(data))
    .on('mouseover', function () {
    var line_path = d3.select('#line path.overlay')[0][0];
    var length_at_point = 0,
        total_length = line_path.getTotalLength(),
        mouse = d3.mouse(line_path);

    // find point on the line to draw tangent on, 
    var INTERVAL = 100;
    while (line_path.getPointAtLength(length_at_point).x < mouse[0] && length_at_point < total_length)
    length_at_point += INTERVAL

    length_at_point -= INTERVAL


    while (line_path.getPointAtLength(length_at_point).x < mouse[0] && length_at_point < total_length) {
        length_at_point++;
    };

    var point = line_path.getPointAtLength(length_at_point),
        prev = {},
        next = {},
        delta = {};


    if (length_at_point > 1 && length_at_point < (total_length - 1)) {
        prev = line_path.getPointAtLength(length_at_point - 1);
        next = line_path.getPointAtLength(length_at_point + 1);
        delta = {
            x: next.x - prev.x,
            y:...