commit c1189d7c3fee2e658b84a191dec28b2bbf8858c2
parent f7b9168c3ad040fae4ab0577aa27a0010f4a31d2
Author: hrryt <harry@mayesfield.uk>
Date: Fri, 14 Aug 2026 23:07:27 +0100
Add blank plots, right angle
Diffstat:
5 files changed, 118 insertions(+), 86 deletions(-)
diff --git a/src/components/graphing/Angle.jsx b/src/components/graphing/Angle.jsx
@@ -1,7 +1,7 @@
import Label from './Label.jsx';
import { useContext } from 'preact/hooks';
import { StrokeWidthContext } from '../../scripts/contexts.js';
-import { getColourClass, getMidpoint, interpolateDistance } from '../../scripts/utils.js';
+import { getColourClass, getMidpoint, interpolateDistance, sumVectors, getVector } from '../../scripts/utils.js';
function getArc(points, radius) {
const start = interpolateDistance(points[1], points[0], radius);
@@ -9,6 +9,12 @@ function getArc(points, radius) {
return [start, end];
}
+function RightAngle({ points, radius, colour }) {
+ const [start, end] = getArc(points, radius);
+ const midpoint = sumVectors(start, getVector(points[1], end));
+ return <polygon points={[points[1], start, midpoint, end]} class={getColourClass(colour)} />
+}
+
function AngleArc({ points, radius, colour }) {
const [start, end] = getArc(points, radius);
return <path d={`M ${start} A ${radius} ${radius} 0 0 1 ${end}`} class={getColourClass(colour)} />;
@@ -19,12 +25,16 @@ function AngleLabel({ points, radius, colour, children }) {
return <Label point={point} colour={colour}>{children}</Label>;
}
-export default function Angle({ points, radius = 0.5, arcs = 1, label = null, colour = "auto" }) {
- const concentricGap = 3 * useContext(StrokeWidthContext);
+export default function Angle({ points, right = false, arcs = 1, label = null, colour = "auto" }) {
+ const strokeWidth = useContext(StrokeWidthContext);
+ const radius = 10 * strokeWidth;
+ const concentricGap = 1.5 * strokeWidth;
return (
<g>
- {Array.from({ length: arcs }).map(
- (_, i) => <AngleArc points={points} radius={(1 + i*concentricGap) * radius} colour={colour} />
+ {right ? (
+ <RightAngle points={points} radius={0.8 * radius} colour={colour} />
+ ) : Array.from({ length: arcs }).map(
+ (_, i) => <AngleArc points={points} radius={(radius + i*concentricGap)} colour={colour} />
)}
{label && <AngleLabel points={points} radius={radius} colour={colour}>{label}</AngleLabel>}
</g>
diff --git a/src/components/graphing/Graph.jsx b/src/components/graphing/Graph.jsx
@@ -49,20 +49,24 @@ function AxisTicks({ viewBox }) {
)
}
-export default function Graph({ children, viewBox, width="300", height="300" }) {
+export default function Plot({ children, mode="graph", viewBox, width="300", height="300" }) {
+ const id = `graph-${Math.floor(1000 * Math.random())}`;
const scaleFactor = Math.max(viewBox[2] / width, viewBox[3] / height);
const strokeWidth = 2.5 * scaleFactor;
const style = `
- text {
+ g#${id} text {
font-size: ${scaleFactor}em;
- stroke-width: ${1.6 * strokeWidth};
+ stroke-width: ${mode === "graph" ? 1.6 * strokeWidth : 0};
}
- path,polygon,line,polyline,circle,ellipse {
+ g#${id} :is(path,polygon,line,polyline,rect,circle,ellipse) {
stroke-width: ${strokeWidth};
}
- path#grid {
+ g#${id} path#grid {
stroke-width: ${0.2 * strokeWidth};
}
+ g#${id} polygon {
+ fill-opacity: ${mode === "graph" ? 0.5 : 0};
+ }
`;
return (
<svg
@@ -70,71 +74,79 @@ export default function Graph({ children, viewBox, width="300", height="300" })
transform="scale(1 -1)" xmlns="http://www.w3.org/2000/svg"
>
<defs>
- <marker
- id='arrowhead'
- viewBox="0 0 4 4"
- refX="0" refY="2"
- markerWidth="4" markerHeight="4"
- orient="auto"
- >
- <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"
- >
- <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 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"
- >
- <polyline class="marker" points="2,1 5,3 2,5" />
- <polyline class="marker" points="5,1 8,3 5,5" />
- </marker>
+ <g>
+ <marker
+ id='arrowhead'
+ viewBox="0 0 4 4"
+ refX="0" refY="2"
+ markerWidth="4" markerHeight="4"
+ orient="auto"
+ >
+ <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"
+ >
+ <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 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"
+ >
+ <polyline class="marker" points="2,1 5,3 2,5" />
+ <polyline class="marker" points="5,1 8,3 5,5" />
+ </marker>
+ </g>
<style>
{style}
</style>
</defs>
<StrokeWidthContext value={strokeWidth}>
- <Grid viewBox={viewBox} />
- <Axes viewBox={viewBox} />
- <AxisTicks viewBox={viewBox} />
- {children}
+ <g id={id}>
+ {mode === "graph" && (
+ <g>
+ <Grid viewBox={viewBox} />
+ <Axes viewBox={viewBox} />
+ <AxisTicks viewBox={viewBox} />
+ </g>
+ )}
+ {children}
+ </g>
</StrokeWidthContext>
</svg>
);
diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx
@@ -7,9 +7,11 @@ 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';
+import Label from '../components/graphing/Label.jsx';
export default function Home() {
const viewBox = [-5, -4, 8, 8];
+ const [A, B, C] = [[1,4], [3,2], [2,1]];
return (
<main>
<Window title="Home">
@@ -18,11 +20,20 @@ export default function Home() {
<Graph viewBox={viewBox}>
<Quadratic a={2} b={1} c={-3} colour="1" viewBox={viewBox} />
<Polygon points={[[1,2], [-1,1], [-4,3]]} label="P" colour="2" />
- <Angle points={[[-4,3], [1,2], [-1,1]]} arcs={2} colour="2" />
<Cubic a={1} b={-3} c={3} d={-1} colour="3" viewBox={viewBox} />
- <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="4" />
+ <Line line={{a: -10, b: -9, c: -1}} colour="4" viewBox={viewBox} />
+ <Point point={[0,1]} label="Q" colour="5" />
+ </Graph>
+ <Graph mode="blank" viewBox={[0, 0, 5, 5]}>
+ <Label point={A} position="top">A</Label>
+ <Label point={B} position="right">B</Label>
+ <Label point={C} position="bottom">C</Label>
+ <Segment points={[A, B]} marker="hash" />
+ <Segment points={[C, B]} marker="double-feather" />
+ <Segment points={[A, C]} />
+ <Angle points={[A, B, C]} right={true} />
+ <Angle points={[B, C, A]} />
+ <Angle points={[C, A, B]} />
</Graph>
</div>
</Window>
diff --git a/src/scripts/utils.js b/src/scripts/utils.js
@@ -74,10 +74,6 @@ export function getColourClass(colour) {
return `color-${colour}`;
}
-function sumVectors(arr) {
- return arr.reduce((a, b) => a.map((ai, i) => ai + b[i]));
-}
-
function scale(vector, factor) {
return vector.map(a => a * factor);
}
@@ -86,21 +82,25 @@ function sum(arr) {
return arr.reduce((a, b) => a + b);
}
-function normalise(vector, scaleFactor = 1) {
- const length = Math.sqrt(sum(vector.map(a => a*a)));
- return scale(vector, scaleFactor / length);
+export function sumVectors(a, b) {
+ return a.map((ai, i) => b[i] + ai);
}
-function getVector(start, end) {
+export function getVector(start, end) {
return start.map((a, i) => end[i] - a);
}
+function normalise(vector, scaleFactor = 1) {
+ const length = Math.sqrt(sum(vector.map(a => a*a)));
+ return scale(vector, scaleFactor / length);
+}
+
function interpolate(start, end, along) {
- return sumVectors([start, scale(getVector(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)]);
+ return sumVectors(start, normalise(getVector(start, end), distance));
}
export function getMidpoint(points) {
diff --git a/src/styles/svg.css b/src/styles/svg.css
@@ -1,4 +1,4 @@
-path,polygon,line,polyline,circle,ellipse {
+path,polygon,line,polyline,rect,circle,ellipse {
stroke-linejoin: round;
stroke-linecap: round;
fill-opacity: 0;
@@ -6,7 +6,6 @@ path,polygon,line,polyline,circle,ellipse {
}
polygon {
- fill-opacity: 0.5;
stroke: black;
fill: black;
}