commit 749b69a4edecfb5e6724775120eba49d9f37fb03
parent c8d1e72f2d81d3410ee5c18ca800c75453bc8f74
Author: hrryt <harry@mayesfield.uk>
Date: Tue, 11 Aug 2026 21:16:37 +0100
Add points, make double transformation QnA
Diffstat:
12 files changed, 84 insertions(+), 38 deletions(-)
diff --git a/src/components/Equation.jsx b/src/components/Equation.jsx
@@ -8,5 +8,5 @@ export default function Equation({ equation, displayMode = false }) {
render(equation, ref.current, { displayMode: displayMode });
}, [equation]);
- return <div ref={ref}>{equation}</div>;
+ return <span ref={ref}>{equation}</span>;
}
diff --git a/src/components/graphing/CoordinateLabel.jsx b/src/components/graphing/CoordinateLabel.jsx
@@ -1,5 +0,0 @@
-import Label from './Label.jsx';
-
-export default function CoordinateLabel({ x, y, position = "top" }) {
- return <Label x={x} y={y} position={position}>{`(${x},${y})`}</Label>;
-}
diff --git a/src/components/graphing/Cubic.jsx b/src/components/graphing/Cubic.jsx
@@ -1,6 +1,6 @@
-import { getSymmetricBounds } from '../../scripts/utils.js';
+import { getSymmetricBounds, getColourClass } from '../../scripts/utils.js';
-export default function Cubic({ a=1, b=0, c=0, d=0, colour="1", viewBox }) {
+export default function Cubic({ a=1, b=0, c=0, d=0, colour="auto", viewBox }) {
// https://math.stackexchange.com/questions/3356084/to-construct-a-polynomial-using-b%C3%A9zier-curves
// http://graphics.stanford.edu/courses/cs164-09-spring/Handouts/handout19.pdf
function g(u, v, w) { return a*u*v*w + b*(v*w + w*u + u*v)/3 + c*(u + v + w)/3 + d; }
@@ -11,5 +11,5 @@ export default function Cubic({ a=1, b=0, c=0, d=0, colour="1", viewBox }) {
const x2 = 1/3 * i + 2/3 * f;
d = `M ${i},${g(i,i,i)} C ${x1},${g(i,i,f)} ${x2},${g(i,f,f)} ${f},${g(f,f,f)}`;
- return <path d={d} class={`color-${colour}`} />
+ return <path d={d} class={getColourClass(colour)} />
}
diff --git a/src/components/graphing/Graph.jsx b/src/components/graphing/Graph.jsx
@@ -60,7 +60,16 @@ export default function Graph({ children, viewBox, width="300", height="300" })
markerUnits="strokeWidth"
orient="auto"
>
- <polygon points="0,0 4,2 0,4" id='arrowhead' />
+ <polygon class='marker' points="0,0 4,2 0,4" />
+ </marker>
+ <marker
+ id="cross"
+ viewBox="0 0 6 6"
+ refX="3" refY="3"
+ markerWidth="6" markerHeight="6"
+ markerUnits="strokeWidth"
+ >
+ <polygon class='marker' points="0,1 1,0 3,2 5,0 6,1 4,3 6,5 5,6 3,4 1,6 0,5 2,3" />
</marker>
<style>
{style}
@@ -68,8 +77,8 @@ export default function Graph({ children, viewBox, width="300", height="300" })
</defs>
<Grid viewBox={viewBox} />
<Axes viewBox={viewBox} />
- {children}
<AxisTicks every={Math.floor(50 * scaleFactor)} viewBox={viewBox} />
+ {children}
</svg>
);
}
diff --git a/src/components/graphing/Label.jsx b/src/components/graphing/Label.jsx
@@ -1,3 +1,5 @@
+import { getColourClass } from '../../scripts/utils.js';
+
function getPosition(position) {
switch (position) {
case 'central':
@@ -17,13 +19,12 @@ function getPosition(position) {
export default function Label({ x, y, position='central', colour='auto', d=.15, children }) {
const [dominantBaseline, textAnchor, dx, dy] = getPosition(position);
- const colourClass = colour == 'auto' ? '' : `color-${colour}`;
return (
<text
x={x} y={-y} dx={d*dx} dy={-d*dy} transform='scale(1 -1)'
dominant-baseline={dominantBaseline}
text-anchor={textAnchor}
- class={colourClass}
+ class={getColourClass(colour)}
>
{children}
</text>
diff --git a/src/components/graphing/Point.jsx b/src/components/graphing/Point.jsx
@@ -0,0 +1,11 @@
+import Label from './Label.jsx';
+import { getColourClass } from '../../scripts/utils.js';
+
+export default function Point({ x, y, label=null, colour="auto", position="top" }) {
+ return (
+ <g>
+ <path marker-start="url(#cross)" d={`M ${x},${y}`} class={getColourClass(colour)}/>
+ {label && <Label x={x} y={y} position={position} d={.3} colour={colour}>{label}</Label>}
+ </g>
+ );
+}
diff --git a/src/components/graphing/Polygon.jsx b/src/components/graphing/Polygon.jsx
@@ -1,4 +1,5 @@
import Label from './Label.jsx';
+import { getColourClass } from '../../scripts/utils.js';
function getMean(arr) {
return arr.reduce((a, b) => a + b) / arr.length;
@@ -15,12 +16,11 @@ function PolygonLabel({ children, points, colour }) {
return <Label x={x} y={y} colour={colour}>{children}</Label>;
}
-export default function Polygon({ points, label=null, colour="1" }) {
+export default function Polygon({ points, label=null, colour="auto" }) {
const pointsString = points.map((point) => point.join(",")).join(" ");
- const colourClass = `color-${colour}`;
return (
<g>
- <polygon points={pointsString} class={colourClass}/>
+ <polygon points={pointsString} class={getColourClass(colour)}/>
{label && <PolygonLabel points={points} colour={colour}>{label}</PolygonLabel>}
</g>
);
diff --git a/src/components/graphing/Quadratic.jsx b/src/components/graphing/Quadratic.jsx
@@ -1,6 +1,6 @@
-import { getSymmetricBounds } from '../../scripts/utils.js';
+import { getSymmetricBounds, getColourClass } from '../../scripts/utils.js';
-export default function Quadratic({ a=1, b=0, c=0, colour="1", viewBox }) {
+export default function Quadratic({ a=1, b=0, c=0, colour="auto", viewBox }) {
function f(x) { return a*x*x + b*x + c; }
const turnX = -b / (2*a);
@@ -10,5 +10,5 @@ export default function Quadratic({ a=1, b=0, c=0, colour="1", viewBox }) {
const controlY = 2*f(turnX) - endY;
const d = `M ${startX},${endY} Q ${turnX},${controlY} ${endX},${endY}`;
- return <path d={d} class={`color-${colour}`} />;
+ return <path d={d} class={getColourClass(colour)} />;
}
diff --git a/src/components/questions/DoubleTransformationQuestion.jsx b/src/components/questions/DoubleTransformationQuestion.jsx
@@ -1,8 +1,9 @@
import { getRandomChoice, getRandomInt } from "../../scripts/utils";
-import CoordinateLabel from "../graphing/CoordinateLabel";
+import Point from "../graphing/Point";
import Polygon from "../graphing/Polygon";
import Graph from "../graphing/Graph";
import Question from "../Question";
+import Equation from "../Equation";
function rotatePoint(point, angle, axis=[0, 0]) {
const diff = [point[0] - axis[0], point[1] - axis[1]]
@@ -15,29 +16,56 @@ export default function DoubleTransformationQuestion({ seed, showAnswer }) {
const viewBox = [-7, -7, 14, 14]
const originalShape = [[0, 1], [2, 1], [0, 2]]
const axis1 = [getRandomInt(-3, 3, seed++), getRandomInt(-3, 3, seed++)]
- const axis2 = [getRandomInt(-3, 3, seed++), getRandomInt(-3, 3, seed++)]
- const angle = getRandomChoice([Math.PI/2, Math.PI, 3*Math.PI/2], seed++)
+ let axis2 = axis1;
+ while (axis2.every((x, i) => x === axis1[i])) {
+ axis2 = [getRandomInt(-3, 3, seed++), getRandomInt(-3, 3, seed++)]
+ }
+ const angleDegrees = getRandomChoice([90, 180, -90], seed++)
+ const angle = angleDegrees / 180 * Math.PI
// Rotation then translation!. could do other way around if really needed.
const midShape = originalShape.map((point) => rotatePoint(point, angle, axis1))
const rotatedAxis = rotatePoint(axis2, angle, axis1)
- const translationVector = axis2.map((value, i) => value - rotatedAxis[i])
+ const translationVector = axis2.map((value, i) => Math.round(value - rotatedAxis[i]))
const endShape = midShape.map((point) => point.map((value, i) => value + translationVector[i]))
+ let angleDirection = ""
+ if (angleDegrees !== 180) {
+ if (angleDegrees < 0) {
+ angleDirection = "counterclockwise"
+ } else {
+ angleDirection = "clockwise"
+ }
+ }
+
+ const angleExpression = (
+ <span>
+ <Equation equation={`${Math.abs(angleDegrees)} degree`} /> {angleDirection}
+ </span>
+ )
+
const question = (<>
- <p>
- What single transformation..?
- </p>
+ <p>Shape P is rotated {angleExpression} about point Q to form shape R.</p>
+ <p>Shape R is translated by <Equation equation={`vec(${translationVector[0]}, ${translationVector[1]})`} /> to form shape S.</p>
+ <p>What single transformation maps shape P onto shape S?</p>
+ <Graph viewBox={viewBox}>
+ <Polygon points={originalShape} label="P" colour="1" />
+ <Point x={axis1[0]} y={axis1[1]} label="Q" colour="2" />
+ </Graph>
+ </>)
+
+ const answer = (<>
+ <p>Rotation {angleExpression} about <Equation equation={`(${axis2[0]}, ${axis2[1]})`} />.</p>
<Graph viewBox={viewBox}>
- <Polygon points={originalShape} label="P" colour="1" />
- <Polygon points={ midShape} label="Q" colour="2" />
- <Polygon points={ endShape} label="R" colour="3" />
- <CoordinateLabel x={axis1[0]} y={axis1[1]} />
- <CoordinateLabel x={axis2[0]} y={axis2[1]} />
+ <Polygon points={originalShape} label="P" colour="1" />
+ <Point x={axis1[0]} y={axis1[1]} label="Q" colour="2" />
+ <Polygon points={ midShape} label="R" colour="3" />
+ <Polygon points={ endShape} label="S" colour="4" />
+ <Point x={axis2[0]} y={axis2[1]} />
</Graph>
</>)
- return <Question question={question} answer="" showAnswer={showAnswer} />
+ return <Question question={question} answer={answer} showAnswer={showAnswer} />
}
diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx
@@ -3,7 +3,6 @@ import Graph from '../components/graphing/Graph.jsx';
import Quadratic from '../components/graphing/Quadratic.jsx';
import Cubic from '../components/graphing/Cubic.jsx';
import Polygon from '../components/graphing/Polygon.jsx';
-import CoordinateLabel from '../components/graphing/CoordinateLabel.jsx';
export default function Home() {
const viewBox = [-5, -4, 8, 8];
@@ -14,9 +13,7 @@ export default function Home() {
<div class="field-border kern">
<Graph viewBox={viewBox}>
<Quadratic a={2} b={1} c={-3} colour="1" viewBox={viewBox} />
- <CoordinateLabel x={-.25} y={-3.125} position="bottom" />
<Polygon points={[[1,2], [-1,1], [-4,3]]} label="P" colour="2" />
- <CoordinateLabel x={-4} y={3} />
<Cubic a={1} b={-3} c={3} d={-1} colour="3" viewBox={viewBox}/>
</Graph>
</div>
diff --git a/src/scripts/utils.js b/src/scripts/utils.js
@@ -35,3 +35,8 @@ export function getSymmetricBounds(midX, viewBox) {
const halfWidth = Math.max(midX - minX, minX + width - midX);
return [midX - halfWidth, midX + halfWidth];
}
+
+export function getColourClass(colour) {
+ if (colour == 'auto') { return ''; }
+ return `color-${colour}`;
+}
diff --git a/src/styles/svg.css b/src/styles/svg.css
@@ -20,13 +20,13 @@ path.axis {
stroke-linecap: butt;
}
-polygon#arrowhead {
+polygon.marker {
fill-opacity: 1;
}
text {
- stroke: white;
- stroke-width: 0.03;
+ stroke: white !important;
+ stroke-width: 0.1;
paint-order: stroke;
}