viewof funcType = Inputs.select(["polynomial", "exponential", "trigonometric"], {
value: "polynomial",
label: "Function type"
})
// Parameters based on function type
viewof a = Inputs.range([-2, 2], {
step: 0.1,
value: funcType === "polynomial" ? 0.5 : funcType === "exponential" ? 1 : 1,
label: tex`a`,
width: 180
})
viewof b = Inputs.range([-3, 3], {
step: 0.1,
value: funcType === "polynomial" ? -1 : funcType === "exponential" ? 0 : 2,
label: tex`b`,
width: 180
})
viewof c = Inputs.range([-2, 2], {
step: 0.1,
value: funcType === "polynomial" ? 2 : funcType === "exponential" ? 0 : 0,
label: tex`c`,
width: 180
})
viewof d = Inputs.range([-2, 2], {
step: 0.1,
value: 1,
label: tex`d`,
width: 180,
disabled: funcType === "exponential"
})
// Point of interest
viewof x0 = Inputs.range([-3, 3], {
step: 0.1,
value: 1,
label: tex`x`,
width: 180
})
// Function definitions
functionData = {
let f, df;
if (funcType === "polynomial") {
f = x => a * x**3 + b * x**2 + c * x + d;
df = x => 3 * a * x**2 + 2 * b * x + c;
} else if (funcType === "exponential") {
f = x => a * Math.exp(x + c) + b;
df = x => a * Math.exp(x + c);
} else { // trigonometric
f = x => a * Math.sin(b * x + c) + d;
df = x => a * b * Math.cos(b * x + c);
}
return { f, df };
}
// Generate function data points
functionPoints = {
const xValues = d3.range(-4, 4.1, 0.1);
return xValues.map(x => ({
x: x,
f: functionData.f(x),
df: functionData.df(x)
}));
}
// Calculate values at point of interest
pointValues = {
const fx0 = functionData.f(x0);
const dfx0 = functionData.df(x0);
return { fx0, dfx0 };
}
// Tangent line points
tangentLine = {
const m = pointValues.dfx0;
const y0 = pointValues.fx0;
return d3.range(x0 - 1, x0 + 1.1, 0.1).map(x => ({
x: x,
y: y0 + m * (x - x0)
}));
}
// Display function and derivative information
html`<div style="text-align: center; font-size: 1.1em; margin: 1.5em 0;">
<p style="margin-bottom: 0.5em;">
${funcType === "polynomial" ?
tex`f(x) = ${a.toFixed(2)}x^3 + ${b.toFixed(2)}x^2 + ${c.toFixed(2)}x + ${d.toFixed(2)}` :
funcType === "exponential" ?
tex`f(x) = ${a.toFixed(2)}e^{x + ${c.toFixed(2)}} + ${b.toFixed(2)}` :
tex`f(x) = ${a.toFixed(2)}\sin(${b.toFixed(2)}x + ${c.toFixed(2)}) + ${d.toFixed(2)}`
}
</p>
<p style="margin-bottom: 0.5em;">
${funcType === "polynomial" ?
tex`f'(x) = ${(3*a).toFixed(2)}x^2 + ${(2*b).toFixed(2)}x + ${c.toFixed(2)}` :
funcType === "exponential" ?
tex`f'(x) = ${a.toFixed(2)}e^{x + ${c.toFixed(2)}}` :
tex`f'(x) = ${(a*b).toFixed(2)}\cos(${b.toFixed(2)}x + ${c.toFixed(2)})`
}
</p>
</div>`Plot.plot({
style: "overflow: visible; display: block; margin: 0 auto;",
width: 800,
height: 600,
grid: true,
x: {label: "x", domain: [-4, 4]},
y: {label: "f(x)", domain: [-8, 8]},
marks: [
// Grid axes
Plot.ruleY([0], {stroke: "#ddd", strokeWidth: 1}),
Plot.ruleX([0], {stroke: "#ddd", strokeWidth: 1}),
// Original function f(x) (blue)
Plot.line(functionPoints, {
x: "x", y: "f",
stroke: "#2166ac", strokeWidth: 3, curve: "basis"
}),
// Derivative function f'(x) (red)
Plot.line(functionPoints, {
x: "x", y: "df",
stroke: "#d73027", strokeWidth: 3, curve: "basis"
}),
// Tangent line at x0 (green)
Plot.line(tangentLine, {
x: "x", y: "y",
stroke: "#1a9850", strokeWidth: 3, strokeDasharray: "6,3"
}),
// Point of interest on f(x)
Plot.dot([{x: x0, y: pointValues.fx0}], {
x: "x", y: "y",
r: 6,
fill: "#2166ac",
stroke: "white", strokeWidth: 2
}),
// Point of interest on f'(x)
Plot.dot([{x: x0, y: pointValues.dfx0}], {
x: "x", y: "y",
r: 6,
fill: "#d73027",
stroke: "white", strokeWidth: 2
}),
// Vertical line showing connection (FIXED)
Plot.ruleX([x0], {
stroke: "#666", strokeWidth: 1, strokeDasharray: "3,3", strokeOpacity: 0.6
}),
// Function labels
Plot.text([
{x: 2.5, y: functionData.f(2.5) + 0.4, text: "f(x)"},
{x: 2.5, y: functionData.df(2.5) + 0.4, text: "f'(x)"},
{x: x0 + 0.7, y: pointValues.fx0 + pointValues.dfx0 * 0.7 + 0.3, text: "tangent line"}
], {
x: "x", y: "y", text: "text",
fontSize: 14, fontWeight: "bold",
fill: d => d.text === "f(x)" ? "#2166ac" :
d.text === "f'(x)" ? "#d73027" : "#1a9850"
}),
// Point labels
Plot.text([
{x: x0 + 0.2, y: pointValues.fx0 + 0.3, text: `(${x0.toFixed(1)}, ${pointValues.fx0.toFixed(2)})`},
{x: x0 + 0.2, y: pointValues.dfx0 - 0.3, text: `slope = ${pointValues.dfx0.toFixed(2)}`}
], {
x: "x", y: "y", text: "text",
fontSize: 11,
fill: d => d.text.includes("slope") ? "#1a9850" : "#333"
})
]
})