commit 2334d6782239763eee94cc35f3b7c6c77cb6ca2d
parent 86207f0a18ec8d5e2e4f37340126b3528654975a
Author: hrryt <harry@mayesfield.uk>
Date: Fri, 14 Aug 2026 13:55:12 +0100
Add segment markers, dynamic label spacing, auto colour support
Diffstat:
9 files changed, 84 insertions(+), 35 deletions(-)
diff --git a/TODO.md b/TODO.md
@@ -1,16 +1,12 @@
# Fixes
- Use KaTeX_Math font outside MathML
-- colour="auto" -> no display?
# Questions
- Improve adding fractions
-- i suppose for a > 1 i should actually factorise 2x-2 etc. factorising code gets bigger
- Add more questions
- Add graphing questions
- Find all roots of this quintic (or something) polynomial (repeated root knowledge)
- Transformations of shapes and graphs (curves)
- - Make text same size for all graph scalings / dynamic axis ticks
- - colour classes and points and polygon labels please :D
- Drop-down for finer controls
- tricky parallel lines / internal angles etc questions
- automate allQuestions using file exploration
diff --git a/src/components/graphing/Angle.jsx b/src/components/graphing/Angle.jsx
@@ -1,5 +1,5 @@
import Label from './Label.jsx';
-import { getColourClass, getMidpoint, interpolate, interpolateDistance } from '../../scripts/utils.js';
+import { getColourClass, getMidpoint, interpolateDistance } from '../../scripts/utils.js';
function getArc(points, radius) {
const start = interpolateDistance(points[1], points[0], radius);
diff --git a/src/components/graphing/Graph.jsx b/src/components/graphing/Graph.jsx
@@ -1,3 +1,4 @@
+import { ScaleFactorContext } from '../../scripts/contexts.js';
import Label from './Label.jsx';
function Grid({ viewBox }) {
@@ -24,17 +25,17 @@ function AxisTicks({ every = 1, viewBox }) {
{Array.from({ length: width - 1 }).map((_, i) => {
const x = i + minX + 1;
if (x == 0 || x % every) { return; }
- return <Label point={[x, 0]} position="bottom" d={.1}>{x}</Label>;
+ return <Label point={[x, 0]} position="bottom">{x}</Label>;
})}
</g>
<g>
{Array.from({ length: height - 1 }).map((_, i) => {
const y = i + minY + 1;
if (y == 0 || y % every) { return; }
- return <Label point={[0, y]} position="left" d={.1}>{y}</Label>;
+ return <Label point={[0, y]} position="left">{y}</Label>;
})}
</g>
- <Label point={[0, 0]} position="bottom-left" d={.1}>0</Label>
+ <Label point={[0, 0]} position="bottom-left">0</Label>
</g>
)
}
@@ -44,6 +45,13 @@ export default function Graph({ children, viewBox, width="300", height="300" })
const style = `
text {
font-size: ${scaleFactor}em;
+ stroke-width: ${4 * scaleFactor};
+ }
+ path,polygon,line,polyline,circle,ellipse {
+ stroke-width: ${2.5 * scaleFactor};
+ }
+ path#grid {
+ stroke-width: ${0.5 * scaleFactor};
}
`;
return (
@@ -63,30 +71,61 @@ export default function Graph({ children, viewBox, width="300", height="300" })
</marker>
<marker
id="cross"
- viewBox="0 0 10 10"
- refX="5" refY="5"
- markerWidth="5" markerHeight="5"
- orient="45"
+ viewBox="0 0 6 6"
+ refX="3" refY="3"
+ markerWidth="6" markerHeight="6"
>
- <polygon class='marker' points="0,4 0,6 4,6 4,10 6,10 6,6 10,6 10,4 4,4 4,0 6,0 6,4" />
+ <line class='marker' x1="1" y1="1" x2="5" y2="5" />
+ <line class='marker' x1="1" y1="5" x2="5" y2="1" />
</marker>
<marker
id="hash"
- viewBox="0 0 10 10"
- refX="5" refY="5"
- markerWidth="5" markerHeight="5"
+ viewBox="0 0 2 6"
+ refX="1" refY="3"
+ markerWidth="2" markerHeight="6"
+ orient="auto"
+ >
+ <line class="marker" x1="1" y1="1" x2="1" y2="5" />
+ </marker>
+ <marker
+ id="double-hash"
+ viewBox="0 0 4 6"
+ refX="2" refY="3"
+ markerWidth="4" markerHeight="6"
+ orient="auto"
+ >
+ <line class="marker" x1="1" y1="1" x2="1" y2="5" />
+ <line class="marker" x1="3" y1="1" x2="3" y2="5" />
+ </marker>
+ <marker
+ id="feather"
+ viewBox="0 0 6 6"
+ refX="5" refY="3"
+ markerWidth="6" markerHeight="6"
+ orient="auto"
+ >
+ <polyline class="marker" points="2,1 5,3 2,5" />
+ </marker>
+ <marker
+ id="double-feather"
+ viewBox="0 0 9 6"
+ refX="5" refY="3"
+ markerWidth="9" markerHeight="6"
orient="auto"
>
- <polygon class="marker" points="4,0 4,10 6,10 6,0" />
+ <polyline class="marker" points="2,1 5,3 2,5" />
+ <polyline class="marker" points="5,1 8,3 5,5" />
</marker>
<style>
{style}
</style>
</defs>
- <Grid viewBox={viewBox} />
- <Axes viewBox={viewBox} />
- <AxisTicks every={Math.floor(50 * scaleFactor)} viewBox={viewBox} />
- {children}
+ <ScaleFactorContext value={scaleFactor}>
+ <Grid viewBox={viewBox} />
+ <Axes viewBox={viewBox} />
+ <AxisTicks every={Math.floor(50 * scaleFactor)} viewBox={viewBox} />
+ {children}
+ </ScaleFactorContext>
</svg>
);
}
diff --git a/src/components/graphing/Label.jsx b/src/components/graphing/Label.jsx
@@ -1,3 +1,5 @@
+import { useContext } from 'preact/hooks';
+import { ScaleFactorContext } from '../../scripts/contexts.js';
import { getColourClass } from '../../scripts/utils.js';
function getPosition(position) {
@@ -17,12 +19,13 @@ function getPosition(position) {
}
}
-export default function Label({ point, position='central', colour='auto', d=.15, children }) {
+export default function Label({ point, position='central', spacing=3, colour='auto', children }) {
const [x, y] = point;
const [dominantBaseline, textAnchor, dx, dy] = getPosition(position);
+ const scaledSpacing = spacing * useContext(ScaleFactorContext);
return (
<text
- x={x} y={-y} dx={d*dx} dy={-d*dy} transform='scale(1 -1)'
+ x={x} y={-y} dx={scaledSpacing * dx} dy={scaledSpacing * -dy} transform='scale(1 -1)'
dominant-baseline={dominantBaseline}
text-anchor={textAnchor}
class={getColourClass(colour)}
diff --git a/src/components/graphing/Point.jsx b/src/components/graphing/Point.jsx
@@ -5,7 +5,7 @@ export default function Point({ point, label=null, colour="auto", position="top"
return (
<g>
<path marker-start="url(#cross)" d={`M ${point}`} class={getColourClass(colour)}/>
- {label && <Label point={point} position={position} d={.3} colour={colour}>{label}</Label>}
+ {label && <Label point={point} position={position} spacing={10} colour={colour}>{label}</Label>}
</g>
);
}
diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx
@@ -6,6 +6,7 @@ import Polygon from '../components/graphing/Polygon.jsx';
import Angle from '../components/graphing/Angle.jsx';
import Segment from '../components/graphing/Segment.jsx';
import Line from '../components/graphing/Line.jsx';
+import Point from '../components/graphing/Point.jsx';
export default function Home() {
const viewBox = [-5, -4, 8, 8];
@@ -19,8 +20,9 @@ export default function Home() {
<Polygon points={[[1,2], [-1,1], [-4,3]]} label="P" colour="2" />
<Angle points={[[-4,3], [1,2], [-1,1]]} double={true} colour="2" />
<Cubic a={1} b={-3} c={3} d={-1} colour="3" viewBox={viewBox} />
- <Segment points={[[0,1], [2,2]]} marker="hash" colour="4" />
<Line line={{a: -10, b: -9, c: -1}} colour="5" viewBox={viewBox} />
+ <Segment points={[[0,1], [2,2]]} marker="double-feather" />
+ <Point point={[0,1]} label="Q" colour="1" />
</Graph>
</div>
</Window>
diff --git a/src/scripts/contexts.js b/src/scripts/contexts.js
@@ -0,0 +1,3 @@
+import { createContext } from 'preact';
+
+export const ScaleFactorContext = createContext(null);
diff --git a/src/scripts/utils.js b/src/scripts/utils.js
@@ -49,10 +49,6 @@ function scale(vector, factor) {
return vector.map(a => a * factor);
}
-export function interpolate(start, end, along) {
- return sumVectors([start, scale(getVector(start, end), along)]);
-}
-
function sum(arr) {
return arr.reduce((a, b) => a + b);
}
@@ -66,6 +62,10 @@ function getVector(start, end) {
return start.map((a, i) => end[i] - a);
}
+function interpolate(start, end, along) {
+ return sumVectors([start, scale(getVector(start, end), along)]);
+}
+
export function interpolateDistance(start, end, distance) {
return sumVectors([start, normalise(getVector(start, end), distance)]);
}
diff --git a/src/styles/svg.css b/src/styles/svg.css
@@ -1,34 +1,40 @@
path,polygon,line,polyline,circle,ellipse {
- stroke-width: 0.06;
stroke-linejoin: round;
stroke-linecap: round;
fill-opacity: 0;
+ stroke: black;
}
polygon {
fill-opacity: 0.5;
+ stroke: black;
+ fill: black;
}
path#grid {
stroke: black;
- stroke-width: 0.01;
}
path.axis {
stroke: black;
- stroke-width: 0.08;
stroke-linecap: butt;
}
-polygon.marker {
- fill-opacity: 1;
+.marker {
stroke: context-stroke;
fill: context-fill;
}
+line.marker, polyline.marker {
+ stroke-width: 1;
+}
+
+polygon.marker {
+ fill-opacity: 1;
+}
+
text {
stroke: white !important;
- stroke-width: 0.1;
paint-order: stroke;
}