Files
gh-jamesrochabrun-skills-ma…/skills/math-teacher/references/calculus.md
2025-11-29 18:49:34 +08:00

13 KiB

Calculus Reference

Advanced calculus concepts with interactive visualizations.

Derivatives

Derivative Calculator

// Numerical derivative using limit definition
function numericalDerivative(f, x, h = 0.0001) {
  return (f(x + h) - f(x - h)) / (2 * h);
}

// Common derivatives
const derivatives = {
  power: (n) => (x) => n * Math.pow(x, n - 1),
  exponential: (a) => (x) => a * Math.pow(Math.E, a * x),
  logarithmic: (x) => 1 / x,
  sine: (x) => Math.cos(x),
  cosine: (x) => -Math.sin(x),
  tangent: (x) => 1 / Math.pow(Math.cos(x), 2)
};

Tangent Line Visualizer

function drawTangentLine(f, x0, canvas) {
  const ctx = canvas.getContext('2d');
  const width = canvas.width;
  const height = canvas.height;

  // Calculate derivative at x0
  const slope = numericalDerivative(f, x0);
  const y0 = f(x0);

  // Draw function
  ctx.strokeStyle = '#667eea';
  ctx.lineWidth = 3;
  ctx.beginPath();

  for (let x = -5; x <= 5; x += 0.1) {
    const y = f(x);
    const px = (x + 5) * (width / 10);
    const py = height - ((y + 5) * (height / 10));

    if (x === -5) ctx.moveTo(px, py);
    else ctx.lineTo(px, py);
  }
  ctx.stroke();

  // Draw tangent line
  ctx.strokeStyle = '#FF6347';
  ctx.lineWidth = 2;
  ctx.beginPath();

  for (let x = -5; x <= 5; x += 0.1) {
    const y = y0 + slope * (x - x0); // Point-slope form
    const px = (x + 5) * (width / 10);
    const py = height - ((y + 5) * (height / 10));

    if (x === -5) ctx.moveTo(px, py);
    else ctx.lineTo(px, py);
  }
  ctx.stroke();

  // Mark point of tangency
  const px0 = (x0 + 5) * (width / 10);
  const py0 = height - ((y0 + 5) * (height / 10));

  ctx.fillStyle = '#FFD700';
  ctx.beginPath();
  ctx.arc(px0, py0, 8, 0, Math.PI * 2);
  ctx.fill();

  // Display slope
  ctx.fillStyle = '#333';
  ctx.font = 'bold 16px Arial';
  ctx.fillText(`Slope = ${slope.toFixed(3)}`, 20, 30);
  ctx.fillText(`f'(${x0.toFixed(2)}) = ${slope.toFixed(3)}`, 20, 50);

  return { slope, y0, x0 };
}

Secant Line Animation

function animateSecantToTangent(f, x0, canvas) {
  const ctx = canvas.getContext('2d');
  let h = 2; // Start with h = 2

  function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Draw function
    drawFunction(f, canvas);

    // Calculate secant slope
    const y0 = f(x0);
    const y1 = f(x0 + h);
    const slope = (y1 - y0) / h;

    // Draw secant line
    drawSecantLine(x0, y0, x0 + h, y1, canvas);

    // Display values
    ctx.fillStyle = '#333';
    ctx.font = 'bold 16px Arial';
    ctx.fillText(`h = ${h.toFixed(4)}`, 20, 30);
    ctx.fillText(`Secant slope = ${slope.toFixed(4)}`, 20, 50);

    if (h > 0.0001) {
      h *= 0.95; // Gradually decrease h
      setTimeout(animate, 50);
    } else {
      ctx.fillText('→ Tangent line!', 20, 70);
      const derivative = numericalDerivative(f, x0);
      ctx.fillText(`Derivative = ${derivative.toFixed(4)}`, 20, 90);
    }
  }

  animate();
}

Rate of Change Explorer

function exploreRateOfChange(scenario, canvas) {
  // Scenarios: distance-time → velocity, velocity-time → acceleration, etc.
  const scenarios = {
    position: {
      f: (t) => 5 * t * t, // Position function
      derivative: 'Velocity',
      units: 'm/s',
      explanation: 'How fast position changes = velocity'
    },
    velocity: {
      f: (t) => 10 * t, // Velocity function
      derivative: 'Acceleration',
      units: 'm/s²',
      explanation: 'How fast velocity changes = acceleration'
    },
    population: {
      f: (t) => 1000 * Math.exp(0.1 * t), // Exponential growth
      derivative: 'Growth Rate',
      units: 'people/year',
      explanation: 'How fast population changes = growth rate'
    }
  };

  const s = scenarios[scenario];
  const ctx = canvas.getContext('2d');

  // Draw both function and derivative
  drawTwoGraphs(s.f, (x) => numericalDerivative(s.f, x), canvas);

  return s;
}

Integrals

Riemann Sum Visualizer

function drawRiemannSum(f, a, b, n, method, canvas) {
  const ctx = canvas.getContext('2d');
  const width = canvas.width;
  const height = canvas.height;
  const dx = (b - a) / n;

  let sum = 0;

  // Draw function
  ctx.strokeStyle = '#667eea';
  ctx.lineWidth = 3;
  ctx.beginPath();

  for (let x = a; x <= b; x += 0.01) {
    const y = f(x);
    const px = ((x - a) / (b - a)) * width;
    const py = height - (y / 10) * height;

    if (x === a) ctx.moveTo(px, py);
    else ctx.lineTo(px, py);
  }
  ctx.stroke();

  // Draw rectangles
  for (let i = 0; i < n; i++) {
    const x = a + i * dx;
    let sampleX;

    switch(method) {
      case 'left':
        sampleX = x;
        break;
      case 'right':
        sampleX = x + dx;
        break;
      case 'midpoint':
        sampleX = x + dx / 2;
        break;
      case 'trapezoid':
        // Will handle separately
        break;
    }

    if (method !== 'trapezoid') {
      const height_rect = f(sampleX);
      sum += height_rect * dx;

      // Draw rectangle
      const px = ((x - a) / (b - a)) * width;
      const pw = (dx / (b - a)) * width;
      const ph = (height_rect / 10) * height;

      ctx.fillStyle = 'rgba(102, 126, 234, 0.3)';
      ctx.fillRect(px, height - ph, pw, ph);
      ctx.strokeStyle = '#667eea';
      ctx.strokeRect(px, height - ph, pw, ph);
    }
  }

  // Display sum
  ctx.fillStyle = '#333';
  ctx.font = 'bold 18px Arial';
  ctx.fillText(`Rectangles: ${n}`, 20, 30);
  ctx.fillText(`Approximate Area: ${sum.toFixed(4)}`, 20, 55);

  return sum;
}

function animateRiemannIncrease(f, a, b, canvas) {
  let n = 1;

  function animate() {
    const sum = drawRiemannSum(f, a, b, n, 'midpoint', canvas);

    if (n < 100) {
      n++;
      setTimeout(animate, 100);
    } else {
      // Show exact integral
      const exact = exactIntegral(f, a, b);
      const ctx = canvas.getContext('2d');
      ctx.fillStyle = '#FF6347';
      ctx.font = 'bold 18px Arial';
      ctx.fillText(`Exact Area: ${exact.toFixed(4)}`, 20, 80);
    }
  }

  animate();
}

Area Under Curve

function calculateDefiniteIntegral(f, a, b, method = 'simpson') {
  const n = 1000; // Number of subdivisions
  const h = (b - a) / n;
  let sum = 0;

  switch(method) {
    case 'simpson':
      // Simpson's Rule (most accurate)
      sum = f(a) + f(b);
      for (let i = 1; i < n; i++) {
        const x = a + i * h;
        sum += (i % 2 === 0 ? 2 : 4) * f(x);
      }
      sum *= h / 3;
      break;

    case 'trapezoidal':
      sum = (f(a) + f(b)) / 2;
      for (let i = 1; i < n; i++) {
        sum += f(a + i * h);
      }
      sum *= h;
      break;

    case 'midpoint':
      for (let i = 0; i < n; i++) {
        sum += f(a + (i + 0.5) * h);
      }
      sum *= h;
      break;
  }

  return sum;
}

Accumulation Function

function drawAccumulationFunction(f, a, canvas) {
  // Draw A(x) = ∫[a to x] f(t) dt
  const ctx = canvas.getContext('2d');
  const width = canvas.width;
  const height = canvas.height;

  // Original function f
  ctx.strokeStyle = '#667eea';
  ctx.lineWidth = 2;
  ctx.beginPath();

  for (let x = -5; x <= 5; x += 0.1) {
    const y = f(x);
    const px = (x + 5) * (width / 10);
    const py = height / 2 - y * 20;

    if (x === -5) ctx.moveTo(px, py);
    else ctx.lineTo(px, py);
  }
  ctx.stroke();

  // Accumulation function A(x)
  ctx.strokeStyle = '#FF6347';
  ctx.lineWidth = 3;
  ctx.beginPath();

  for (let x = -5; x <= 5; x += 0.1) {
    const y = calculateDefiniteIntegral(f, a, x);
    const px = (x + 5) * (width / 10);
    const py = height / 2 - y * 20;

    if (x === -5) ctx.moveTo(px, py);
    else ctx.lineTo(px, py);
  }
  ctx.stroke();

  // Labels
  ctx.fillStyle = '#333';
  ctx.font = '16px Arial';
  ctx.fillText('f(x) - Original function', 20, 30);
  ctx.fillStyle = '#FF6347';
  ctx.fillText('A(x) = ∫f(t)dt - Accumulation', 20, 55);
}

Limits

Limit Visualizer

function visualizeLimit(f, a, canvas) {
  const ctx = canvas.getContext('2d');
  const width = canvas.width;
  const height = canvas.height;

  // Draw function
  ctx.strokeStyle = '#667eea';
  ctx.lineWidth = 3;
  ctx.beginPath();

  let leftLimit = null;
  let rightLimit = null;

  for (let x = -5; x <= 5; x += 0.01) {
    if (Math.abs(x - a) > 0.001) { // Skip the point
      const y = f(x);
      if (isFinite(y)) {
        const px = (x + 5) * (width / 10);
        const py = height / 2 - y * 20;

        if (x === -5) ctx.moveTo(px, py);
        else ctx.lineTo(px, py);

        // Track limits
        if (x < a && x > a - 0.1) leftLimit = y;
        if (x > a && x < a + 0.1) rightLimit = y;
      }
    }
  }
  ctx.stroke();

  // Mark point of interest
  const px = (a + 5) * (width / 10);
  const py_left = height / 2 - leftLimit * 20;
  const py_right = height / 2 - rightLimit * 20;

  // Open circles for limit points
  ctx.strokeStyle = '#FF6347';
  ctx.lineWidth = 3;
  ctx.beginPath();
  ctx.arc(px, py_left, 6, 0, Math.PI * 2);
  ctx.stroke();

  // Display limit values
  ctx.fillStyle = '#333';
  ctx.font = 'bold 16px Arial';
  ctx.fillText(`lim (x→${a}⁻) = ${leftLimit?.toFixed(3)}`, 20, 30);
  ctx.fillText(`lim (x→${a}⁺) = ${rightLimit?.toFixed(3)}`, 20, 55);

  const limitExists = Math.abs(leftLimit - rightLimit) < 0.01;
  if (limitExists) {
    ctx.fillStyle = '#4CAF50';
    ctx.fillText(`Limit exists: ${leftLimit.toFixed(3)}`, 20, 80);
  } else {
    ctx.fillStyle = '#FF6347';
    ctx.fillText('Limit does not exist', 20, 80);
  }

  return { leftLimit, rightLimit, limitExists };
}

Interactive Epsilon-Delta

function visualizeEpsilonDelta(f, a, L, epsilon, canvas) {
  const ctx = canvas.getContext('2d');

  // Draw function
  drawFunction(f, canvas);

  // Draw epsilon band
  const py_upper = height / 2 - (L + epsilon) * 20;
  const py_lower = height / 2 - (L - epsilon) * 20;

  ctx.fillStyle = 'rgba(255, 99, 71, 0.2)';
  ctx.fillRect(0, py_upper, width, py_lower - py_upper);

  // Find delta
  let delta = 0.1;
  for (let d = 0.01; d < 5; d += 0.01) {
    const y_left = f(a - d);
    const y_right = f(a + d);

    if (Math.abs(y_left - L) < epsilon && Math.abs(y_right - L) < epsilon) {
      delta = d;
    } else {
      break;
    }
  }

  // Draw delta interval
  const px_left = (a - delta + 5) * (width / 10);
  const px_right = (a + delta + 5) * (width / 10);

  ctx.fillStyle = 'rgba(102, 126, 234, 0.2)';
  ctx.fillRect(px_left, 0, px_right - px_left, height);

  // Labels
  ctx.fillStyle = '#333';
  ctx.font = 'bold 16px Arial';
  ctx.fillText(`ε = ${epsilon.toFixed(3)}`, 20, 30);
  ctx.fillText(`δ = ${delta.toFixed(3)}`, 20, 55);
  ctx.fillText(`For |x - ${a}| < δ, |f(x) - ${L}| < ε`, 20, 80);

  return delta;
}

Optimization

Critical Points Finder

function findCriticalPoints(f, a, b) {
  const points = [];
  const step = (b - a) / 1000;

  for (let x = a; x <= b; x += step) {
    const derivative = numericalDerivative(f, x);

    // Check if derivative is close to zero
    if (Math.abs(derivative) < 0.01) {
      const secondDerivative = numericalDerivative(
        (t) => numericalDerivative(f, t),
        x
      );

      points.push({
        x: x,
        y: f(x),
        type: secondDerivative < 0 ? 'local maximum' :
              secondDerivative > 0 ? 'local minimum' :
              'inflection point',
        derivative: derivative,
        secondDerivative: secondDerivative
      });
    }
  }

  return points;
}

function visualizeOptimization(f, a, b, canvas) {
  const ctx = canvas.getContext('2d');

  // Draw function
  drawFunction(f, canvas);

  // Find and mark critical points
  const criticalPoints = findCriticalPoints(f, a, b);

  criticalPoints.forEach(point => {
    const px = (point.x + 5) * (width / 10);
    const py = height / 2 - point.y * 20;

    // Different colors for different types
    ctx.fillStyle = point.type.includes('maximum') ? '#4CAF50' :
                    point.type.includes('minimum') ? '#FF6347' :
                    '#FFD700';

    ctx.beginPath();
    ctx.arc(px, py, 8, 0, Math.PI * 2);
    ctx.fill();

    // Label
    ctx.fillStyle = '#333';
    ctx.font = '12px Arial';
    ctx.fillText(`${point.type}`, px + 10, py - 10);
    ctx.fillText(`(${point.x.toFixed(2)}, ${point.y.toFixed(2)})`,
                 px + 10, py + 5);
  });

  return criticalPoints;
}
function visualizeRelatedRates(scenario, rate, time, canvas) {
  const scenarios = {
    ladder: {
      // Ladder sliding down wall
      length: 10, // Ladder length
      rate: rate, // dx/dt (horizontal speed)
      draw: function(t) {
        const x = rate * t;
        const y = Math.sqrt(this.length * this.length - x * x);
        const dy_dt = -(x * rate) / y; // dy/dt calculated

        drawLadder(x, y, canvas);
        return { x, y, dx_dt: rate, dy_dt };
      }
    },
    balloon: {
      // Expanding balloon
      rate: rate, // dr/dt (radius growth rate)
      draw: function(t) {
        const r = 1 + rate * t;
        const V = (4/3) * Math.PI * r * r * r;
        const dV_dt = 4 * Math.PI * r * r * rate; // dV/dt

        drawBalloon(r, canvas);
        return { r, V, dr_dt: rate, dV_dt };
      }
    }
  };

  return scenarios[scenario].draw(time);
}

Summary

Calculus patterns provide:

  • Visual derivative calculations with tangent lines
  • Riemann sum animations showing integral approximation
  • Limit visualization with epsilon-delta
  • Critical point finding for optimization
  • Related rates scenarios with animations

These tools make abstract calculus concepts concrete and interactive!