Skip to content

Commit 63c7fa7

Browse files
committed
Add Parabola equation solver Calculator as a shortcode
1 parent d7d0ba7 commit 63c7fa7

3 files changed

Lines changed: 377 additions & 2 deletions

File tree

content/_index.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ width: normal
1010

1111
{{< home/home >}}
1212

13-
{{< home/quad_eq >}}
13+
{{< home/quad_eq >}}
14+
15+
{{< home/parabola_solver >}}

content/exercises/graded-assignments/statistics-1/q1.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,23 @@ This solves the equation $x^2 + x + 1 = 0$.
5353

5454
### Example 4: A Blank Solver
5555
Try your own coefficients below.
56-
{{< home/quad_eq >}}
56+
{{< home/quad_eq >}}
57+
58+
59+
This interactive tool uses pure JavaScript to find the vertex, focus, and directrix for any parabola in the form $y = ax^2 + bx + c$.
60+
61+
### Example 1: A simple parabola
62+
Solve for the properties of $y = 0.5x^2 + 2x - 3$.
63+
{{< home/parabola_solver a="0.5" b="2" c="-3" >}}
64+
65+
---
66+
67+
### Example 2: Another example
68+
Solve for the properties of $y = -x^2 + 6x - 8$.
69+
{{< home/parabola_solver a="-1" b="6" c="-8" >}}
70+
71+
---
72+
73+
### Try your own
74+
Enter your own coefficients below.
75+
{{< home/parabola_solver >}}
Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
{{/*
2+
Hugo Shortcode to solve for the vertex, focus, and directrix of a parabola.
3+
It takes the coefficients a, b, and c for the equation y = ax^2 + bx + c.
4+
5+
This version uses pure JavaScript for calculations, no external library needed.
6+
7+
Usage:
8+
{{< parabola_solver_js a="1" b="-4" c="5" >}}
9+
This solves y = x^2 - 4x + 5.
10+
*/}}
11+
12+
<style>
13+
/* Scoped CSS for the parabola solver (same as before for consistency) */
14+
.parabola-solver-container {
15+
max-width: 650px;
16+
margin: 2rem auto;
17+
padding: 2rem;
18+
background-color: #fcfcfc;
19+
border: 1px solid #e0e0e0;
20+
border-radius: 12px;
21+
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
22+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
23+
color: #333;
24+
}
25+
26+
.parabola-solver-container h3 {
27+
text-align: center;
28+
color: #0056b3;
29+
margin-bottom: 1.5rem;
30+
font-size: 1.8rem;
31+
}
32+
33+
.equation-display {
34+
font-family: 'Times New Roman', Times, serif;
35+
font-style: italic;
36+
font-size: 1.8rem;
37+
text-align: center;
38+
margin-bottom: 2rem;
39+
color: #1a1a1a;
40+
background-color: #f0f8ff;
41+
padding: 1rem;
42+
border-radius: 8px;
43+
border: 1px dashed #cce5ff;
44+
}
45+
46+
.input-grid {
47+
display: grid;
48+
grid-template-columns: repeat(3, 1fr);
49+
gap: 1.5rem;
50+
justify-content: center;
51+
margin-bottom: 2rem;
52+
}
53+
54+
.input-group {
55+
display: flex;
56+
flex-direction: column;
57+
align-items: center;
58+
}
59+
60+
.input-group label {
61+
font-weight: 600;
62+
margin-bottom: 0.5rem;
63+
color: #555;
64+
}
65+
66+
.input-group input[type="number"] {
67+
width: 100%;
68+
max-width: 120px;
69+
padding: 0.8rem;
70+
border: 1px solid #ccc;
71+
border-radius: 6px;
72+
font-size: 1.1rem;
73+
text-align: center;
74+
transition: border-color 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
75+
}
76+
77+
.input-group input:focus {
78+
outline: none;
79+
border-color: #007bff;
80+
box-shadow: 0 0 8px rgba(0, 123, 255, 0.4);
81+
}
82+
83+
.solver-button-container {
84+
text-align: center;
85+
}
86+
87+
.solver-button {
88+
padding: 1rem 2.5rem;
89+
background-color: #007bff;
90+
color: #fff;
91+
border: none;
92+
border-radius: 8px;
93+
font-size: 1.1rem;
94+
font-weight: bold;
95+
cursor: pointer;
96+
transition: background-color 0.3s ease-in-out, transform 0.1s ease-in-out;
97+
}
98+
99+
.solver-button:hover {
100+
background-color: #0056b3;
101+
transform: translateY(-2px);
102+
}
103+
104+
.solver-result {
105+
margin-top: 2.5rem;
106+
padding: 1.8rem;
107+
background-color: #eaf6ff;
108+
border-radius: 10px;
109+
border: 1px solid #b3d9ff;
110+
line-height: 1.8;
111+
font-size: 1.1rem;
112+
color: #1a2a3a;
113+
}
114+
115+
.result-item {
116+
margin-bottom: 0.8rem;
117+
}
118+
119+
.result-label {
120+
font-weight: 700;
121+
color: #0056b3;
122+
}
123+
124+
.result-value {
125+
font-family: 'Courier New', Courier, monospace;
126+
background-color: #fff;
127+
padding: 0.2em 0.5em;
128+
border-radius: 4px;
129+
border: 1px solid #d4e3f1;
130+
}
131+
132+
.solver-error {
133+
color: #dc3545;
134+
font-weight: bold;
135+
}
136+
137+
/* Dark mode support */
138+
html:is(.dark) {
139+
.parabola-solver-container {
140+
background-color: #181c24;
141+
border-color: #232a36;
142+
color: #e0e6ef;
143+
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.32);
144+
}
145+
146+
.parabola-solver-container h3 {
147+
color: #7bb6ff;
148+
}
149+
150+
.equation-display {
151+
background-color: #232a36;
152+
color: #e0e6ef;
153+
border-color: #35507a;
154+
}
155+
156+
.input-group label {
157+
color: #b3c7e6;
158+
}
159+
160+
.input-group input[type="number"] {
161+
background-color: #232a36;
162+
color: #e0e6ef;
163+
border-color: #35507a;
164+
}
165+
166+
.input-group input:focus {
167+
border-color: #339cff;
168+
box-shadow: 0 0 8px rgba(51, 156, 255, 0.4);
169+
}
170+
171+
.solver-button {
172+
background-color: #339cff;
173+
color: #fff;
174+
}
175+
176+
.solver-button:hover {
177+
background-color: #1976d2;
178+
}
179+
180+
.solver-result {
181+
background-color: #1a273a;
182+
border-color: #35507a;
183+
color: #e0e6ef;
184+
}
185+
186+
.result-label {
187+
color: #7bb6ff;
188+
}
189+
190+
.result-value {
191+
background-color: #232a36;
192+
color: #e0e6ef;
193+
border-color: #35507a;
194+
}
195+
196+
.solver-error {
197+
color: #ff6b6b;
198+
}
199+
}
200+
201+
@media (max-width: 600px) {
202+
.parabola-solver-container {
203+
padding: 1rem;
204+
max-width: 98vw;
205+
}
206+
.equation-display {
207+
font-size: 1.2rem;
208+
padding: 0.6rem;
209+
}
210+
.input-grid {
211+
grid-template-columns: 1fr;
212+
gap: 1rem;
213+
}
214+
.solver-button {
215+
width: 100%;
216+
padding: 0.8rem 0;
217+
font-size: 1rem;
218+
}
219+
.solver-result {
220+
padding: 1rem;
221+
font-size: 1rem;
222+
}
223+
}
224+
</style>
225+
226+
<div class="parabola-solver-container">
227+
<h3>Parabola Properties Calculator (JS Only)</h3>
228+
229+
<div class="equation-display">
230+
y = <span id="a-display-{{ .Ordinal }}">a</span>x² + <span id="b-display-{{ .Ordinal }}">b</span>x + <span id="c-display-{{ .Ordinal }}">c</span>
231+
</div>
232+
233+
<div class="input-grid">
234+
<div class="input-group">
235+
<label for="a-input-{{ .Ordinal }}">a:</label>
236+
<input type="number" id="a-input-{{ .Ordinal }}" value="{{ .Get "a" | default "1" }}" oninput="updateParabolaDisplayJS('a', this.value, '{{ .Ordinal }}')">
237+
</div>
238+
<div class="input-group">
239+
<label for="b-input-{{ .Ordinal }}">b:</label>
240+
<input type="number" id="b-input-{{ .Ordinal }}" value="{{ .Get "b" | default "0" }}" oninput="updateParabolaDisplayJS('b', this.value, '{{ .Ordinal }}')">
241+
</div>
242+
<div class="input-group">
243+
<label for="c-input-{{ .Ordinal }}">c:</label>
244+
<input type="number" id="c-input-{{ .Ordinal }}" value="{{ .Get "c" | default "0" }}" oninput="updateParabolaDisplayJS('c', this.value, '{{ .Ordinal }}')">
245+
</div>
246+
</div>
247+
248+
<div class="solver-button-container">
249+
<button class="solver-button" onclick="calculateParabolaPropertiesJS('{{ .Ordinal }}')">Calculate Properties</button>
250+
</div>
251+
252+
<div class="solver-result" id="results-output-{{ .Ordinal }}">
253+
Results will appear here.
254+
</div>
255+
</div>
256+
257+
<script>
258+
// Pure JavaScript functions for calculations and display updates.
259+
// Use `window.` to make them globally accessible from `onclick` and `oninput`.
260+
261+
/**
262+
* Updates the displayed coefficient in the equation.
263+
* @param {string} param The coefficient name ('a', 'b', or 'c').
264+
* @param {string} value The new value from the input.
265+
* @param {string} ordinal The unique ID part for the shortcode instance.
266+
*/
267+
window.updateParabolaDisplayJS = function(param, value, ordinal) {
268+
const displayElement = document.getElementById(param + '-display-' + ordinal);
269+
if (displayElement) {
270+
displayElement.textContent = value;
271+
}
272+
}
273+
274+
/**
275+
* Formats a number to a fixed number of decimal places.
276+
* @param {number} num The number to format.
277+
* @param {number} precision The number of decimal places.
278+
* @returns {string} The formatted number string.
279+
*/
280+
function formatNumber(num, precision = 6) {
281+
if (typeof num !== 'number' || isNaN(num)) {
282+
return 'N/A';
283+
}
284+
// Use toFixed to control decimal places.
285+
return num.toFixed(precision);
286+
}
287+
288+
/**
289+
* Calculates the parabola properties (vertex, focus, directrix).
290+
* @param {string} ordinal The unique ID part for the shortcode instance.
291+
*/
292+
window.calculateParabolaPropertiesJS = function(ordinal) {
293+
const aInput = document.getElementById('a-input-' + ordinal);
294+
const bInput = document.getElementById('b-input-' + ordinal);
295+
const cInput = document.getElementById('c-input-' + ordinal);
296+
const resultsOutput = document.getElementById('results-output-' + ordinal);
297+
298+
const a = parseFloat(aInput.value);
299+
const b = parseFloat(bInput.value);
300+
const c = parseFloat(cInput.value);
301+
302+
try {
303+
if (isNaN(a) || isNaN(b) || isNaN(c)) {
304+
throw new Error("Please enter valid numbers for a, b, and c.");
305+
}
306+
307+
if (a === 0) {
308+
resultsOutput.innerHTML = '<span class="solver-error">Error: "a" cannot be 0. This is a linear equation, not a parabola.</span>';
309+
return;
310+
}
311+
312+
// --- Calculation Formulas using standard JavaScript ---
313+
// Vertex x-coordinate: -b / 2a
314+
const vertexX = -b / (2 * a);
315+
316+
// Vertex y-coordinate: plug vertexX back into the equation y = ax^2 + bx + c
317+
const vertexY = (a * Math.pow(vertexX, 2)) + (b * vertexX) + c;
318+
319+
// Focus y-coordinate: (4ac - b^2 + 1) / 4a
320+
const focusY = (4 * a * c - Math.pow(b, 2) + 1) / (4 * a);
321+
322+
// Directrix y-value: y = (4ac - b^2 - 1) / 4a
323+
const directrixY = (4 * a * c - Math.pow(b, 2) - 1) / (4 * a);
324+
325+
// Format the results for display
326+
const vertexXFormatted = formatNumber(vertexX);
327+
const vertexYFormatted = formatNumber(vertexY);
328+
const focusYFormatted = formatNumber(focusY);
329+
const directrixYFormatted = formatNumber(directrixY);
330+
331+
const resultHTML = `
332+
<div class="result-item"><span class="result-label">Vertex:</span> <span class="result-value">(${vertexXFormatted}, ${vertexYFormatted})</span></div>
333+
<div class="result-item"><span class="result-label">Focus:</span> <span class="result-value">(${vertexXFormatted}, ${focusYFormatted})</span></div>
334+
<div class="result-item"><span class="result-label">Directrix:</span> <span class="result-value">y = ${directrixYFormatted}</span></div>
335+
`;
336+
resultsOutput.innerHTML = resultHTML;
337+
338+
} catch (error) {
339+
resultsOutput.innerHTML = `<span class="solver-error">Error: ${error.message}</span>`;
340+
console.error("Parabola solver error:", error);
341+
}
342+
}
343+
344+
// Initial update of the display when the shortcode loads
345+
document.addEventListener('DOMContentLoaded', () => {
346+
// Use a timeout to ensure all elements are loaded before updating.
347+
setTimeout(() => {
348+
const ordinal = '{{ .Ordinal }}';
349+
updateParabolaDisplayJS('a', document.getElementById('a-input-' + ordinal).value, ordinal);
350+
updateParabolaDisplayJS('b', document.getElementById('b-input-' + ordinal).value, ordinal);
351+
updateParabolaDisplayJS('c', document.getElementById('c-input-' + ordinal).value, ordinal);
352+
}, 100);
353+
});
354+
</script>

0 commit comments

Comments
 (0)