Skip to content

Commit d84800f

Browse files
[rotated-axis-labels] New tests and comments
1 parent 015ff70 commit d84800f

File tree

2 files changed

+109
-28
lines changed

2 files changed

+109
-28
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import {getLabelPosition, fontSize} from "./axis-labels";
2+
3+
import type {GraphDimensions} from "../types";
4+
5+
describe("getLabelPosition", () => {
6+
it("should return the correct position for the default graph", () => {
7+
const graphInfo: GraphDimensions = {
8+
range: [
9+
[-10, 10],
10+
[-10, 10],
11+
],
12+
width: 400,
13+
height: 400,
14+
};
15+
const labelLocation = "onAxis";
16+
const expected = [
17+
[400, 200], // X Label at [Right edge of the graph, vertical center of the graph]
18+
[200, -2 * fontSize], // Y Label at [Horizontal center of the graph, 2x fontSize above the top edge]
19+
];
20+
21+
expect(getLabelPosition(graphInfo, labelLocation)).toEqual(expected);
22+
});
23+
24+
it("should return the correct position for labels set to alongEdge", () => {
25+
const graphInfo: GraphDimensions = {
26+
range: [
27+
[-10, 10],
28+
[-10, 10],
29+
],
30+
width: 400,
31+
height: 400,
32+
};
33+
const labelLocation = "alongEdge";
34+
const expected = [
35+
[200, 400 + fontSize], // X Label at [Horizontal center of the graph, 1x fontSize below the bottom edge]
36+
[-fontSize, 200 - fontSize], // Y label at [1x fontSize to the left of the left edge, vertical center of the graph]
37+
];
38+
39+
expect(getLabelPosition(graphInfo, labelLocation)).toEqual(expected);
40+
});
41+
42+
it("should return the correct position for labels set to alongEdge with wholly negative ranges", () => {
43+
const graphInfo: GraphDimensions = {
44+
range: [
45+
[-10, -5],
46+
[-10, -5],
47+
],
48+
width: 400,
49+
height: 400,
50+
};
51+
const labelLocation = "alongEdge";
52+
const expected = [
53+
[200, 400 + fontSize], // X Label at [Horizontal center of the graph, 1x fontSize below the bottom edge]
54+
[-14, 200 - fontSize], // Y label at [1x fontSize to the left of the left edge, vertical center of the graph]
55+
];
56+
57+
expect(getLabelPosition(graphInfo, labelLocation)).toEqual(expected);
58+
});
59+
60+
it("should return the correct position for labels set to alongEdge with wholly positive ranges", () => {
61+
const graphInfo: GraphDimensions = {
62+
range: [
63+
[5, 10],
64+
[5, 10],
65+
],
66+
width: 400,
67+
height: 400,
68+
};
69+
const labelLocation = "alongEdge";
70+
const expected = [
71+
[200, 400 + 3 * fontSize], // X Label at [Horizontal center of the graph, 3x fontSize below the bottom edge]
72+
[-3 * fontSize, 200 - fontSize], // Y label at [3x fontSize to the left of the left edge, vertical center of the graph]
73+
];
74+
75+
expect(getLabelPosition(graphInfo, labelLocation)).toEqual(expected);
76+
});
77+
});

packages/perseus/src/widgets/interactive-graphs/backgrounds/axis-labels.tsx

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,23 @@ import {replaceOutsideTeX} from "../utils";
1010
import type {I18nContextType} from "../../../components/i18n-context";
1111
import type {GraphConfig} from "../reducer/use-graph-config";
1212
import type {GraphDimensions} from "../types";
13-
import type {Interval} from "mafs";
1413

15-
const fontSize = 14;
14+
// Exported for testing purposes
15+
export const fontSize = 14;
16+
1617
export default function AxisLabels({i18n}: {i18n: I18nContextType}) {
1718
const {range, labels, width, height, labelLocation} = useGraphConfig();
1819
const {strings} = i18n;
1920

20-
// Get the position of the main axis labels
21-
const [xAxisLabelLocation, yAxisLabelLocation] = getLabelPosition(
21+
const graphInfo: GraphDimensions = {
2222
range,
2323
width,
2424
height,
25+
};
26+
27+
// Get the position of the main axis labels
28+
const [xAxisLabelLocation, yAxisLabelLocation] = getLabelPosition(
29+
graphInfo,
2530
labelLocation,
2631
);
2732

@@ -72,24 +77,16 @@ export default function AxisLabels({i18n}: {i18n: I18nContextType}) {
7277
}
7378

7479
export const getLabelPosition = (
75-
range: [Interval, Interval],
76-
width: number,
77-
height: number,
80+
graphInfo: GraphDimensions,
7881
labelLocation: GraphConfig["labelLocation"],
7982
): vec.Vector2[] => {
80-
// If the labels are rotated, we need to calculate the position
81-
// of the labels based on the graph's dimensions and range.
82-
const graphInfo: GraphDimensions = {
83-
range,
84-
width,
85-
height,
86-
};
87-
8883
switch (labelLocation) {
84+
// If the labels are placed on the axes (default), we need to place them at the
85+
// end of the axis, which is the maximum value of the axis.
8986
case "onAxis": {
90-
const xLabelInitial: vec.Vector2 = [range[X][MAX], 0];
91-
const yLabelInitial: vec.Vector2 = [0, range[Y][MAX]];
92-
const yLabelOffset: vec.Vector2 = [0, -fontSize * 2];
87+
const xLabelInitial: vec.Vector2 = [graphInfo.range[X][MAX], 0];
88+
const yLabelInitial: vec.Vector2 = [0, graphInfo.range[Y][MAX]];
89+
const yLabelOffset: vec.Vector2 = [0, -fontSize * 2]; // Move the y-axis label up by 2 font sizes
9390

9491
const xLabel = pointToPixel(xLabelInitial, graphInfo);
9592
const yLabel = vec.add(
@@ -98,25 +95,31 @@ export const getLabelPosition = (
9895
);
9996
return [xLabel, yLabel];
10097
}
98+
// If the labels are placed along the edges of the graph, we need to rotate them
99+
// and place them at the center of the left and bottom edges of the graph.
101100
case "alongEdge": {
101+
// Offset the labels by a certain amount based on the range of the graph, to ensure that
102+
// the labels do not overlap with the axis tick if they are out of the graph bounds.
102103
const xAxisLabelOffset: [number, number] =
103-
range[Y][MIN] >= 0 // Move the label down by 3 font sizes if the y-axis is left of the graph
104-
? [0, fontSize * 3]
105-
: [0, fontSize];
104+
graphInfo.range[Y][MIN] >= 0
105+
? [0, fontSize * 3] // Move the label down by 3 font sizes if the y-axis min is positive
106+
: [0, fontSize]; // Move the label down by 1 font size if the y-axis min is negative
106107
const yAxisLabelOffset: [number, number] =
107-
range[X][MIN] >= 0 // Move the label left by 3.5 font sizes if the x-axis is below the graph
108-
? [-fontSize * 3.5, -fontSize]
109-
: [-fontSize, -fontSize];
108+
graphInfo.range[X][MIN] >= 0
109+
? [-fontSize * 3, -fontSize] // Move the label left by 3.5 font sizes if the x-axis min is positive
110+
: [-fontSize, -fontSize]; // Move the label left by 1 font size if the x-axis min is negative
110111

112+
// Calculate the location of the labels to be halfway between the min and max values of the axes
111113
const xAxisLabelLocation: vec.Vector2 = [
112-
(range[X][MIN] + range[X][MAX]) / 2,
113-
range[Y][MIN],
114+
(graphInfo.range[X][MIN] + graphInfo.range[X][MAX]) / 2,
115+
graphInfo.range[Y][MIN],
114116
];
115117
const yAxisLabelLocation: vec.Vector2 = [
116-
range[X][MIN],
117-
(range[Y][MIN] + range[Y][MAX]) / 2,
118+
graphInfo.range[X][MIN],
119+
(graphInfo.range[Y][MIN] + graphInfo.range[Y][MAX]) / 2,
118120
];
119121

122+
// Convert the Vector2 coordinates to pixel coordinates and add the offsets
120123
const xLabel = vec.add(
121124
pointToPixel(xAxisLabelLocation, graphInfo),
122125
xAxisLabelOffset,
@@ -125,6 +128,7 @@ export const getLabelPosition = (
125128
pointToPixel(yAxisLabelLocation, graphInfo),
126129
yAxisLabelOffset,
127130
);
131+
128132
return [xLabel, yLabel];
129133
}
130134
// Add more cases for other label locations as needed

0 commit comments

Comments
 (0)