Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eight-stingrays-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/perseus": minor
---

Add logarithm graph state management and reducer for supporting Logarithm graph in Interactive Graph
1 change: 1 addition & 0 deletions packages/perseus/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export {
getAbsoluteValueCoords,
getCircleCoords,
getExponentialCoords,
getLogarithmCoords,
getLineCoords,
getLinearSystemCoords,
getPointCoords,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,16 @@ class InteractiveGraphQuestionBuilder {
return this;
}

withLogarithm(options?: {
coords?: [Coord, Coord];
asymptote?: number;
startCoords?: [Coord, Coord];
startAsymptote?: number;
}): InteractiveGraphQuestionBuilder {
this.interactiveFigureConfig = new LogarithmGraphConfig(options);
return this;
}

withAbsoluteValue(options?: {
coords?: [Coord, Coord];
startCoords?: [Coord, Coord];
Expand Down Expand Up @@ -864,6 +874,46 @@ class ExponentialGraphConfig implements InteractiveFigureConfig {
}
}

class LogarithmGraphConfig implements InteractiveFigureConfig {
private coords?: [Coord, Coord];
private asymptote?: number;
private startCoords?: [Coord, Coord];
private startAsymptote?: number;

constructor(options?: {
coords?: [Coord, Coord];
asymptote?: number;
startCoords?: [Coord, Coord];
startAsymptote?: number;
}) {
this.coords = options?.coords;
this.asymptote = options?.asymptote;
this.startCoords = options?.startCoords;
this.startAsymptote = options?.startAsymptote;
}

correct(): PerseusGraphType {
return {
type: "logarithm",
coords: this.coords,
asymptote: this.asymptote,
};
}

graph(): PerseusGraphType {
return {
type: "logarithm",
startCoords:
this.startCoords != null
? {
coords: this.startCoords,
asymptote: this.startAsymptote ?? 0,
}
: undefined,
};
}
}

class TangentGraphConfig implements InteractiveFigureConfig {
private coords?: [Coord, Coord];
private startCoords?: [Coord, Coord];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ import {
sinusoidQuestionWithDefaultCorrect,
tangentQuestion,
tangentQuestionWithDefaultCorrect,
logarithmQuestion,
logarithmQuestionWithDefaultCorrect,
sinusoidWithPiTicks,
unlimitedPointQuestion,
unlimitedPolygonQuestion,
Expand Down Expand Up @@ -239,6 +241,7 @@ describe("Interactive Graph", function () {
quadratic: quadraticQuestion,
sinusoid: sinusoidQuestion,
tangent: tangentQuestion,
logarithm: logarithmQuestion,
"unlimited-point": pointQuestion,
"unlimited-polygon": polygonQuestion,
};
Expand All @@ -257,6 +260,7 @@ describe("Interactive Graph", function () {
quadratic: quadraticQuestionWithDefaultCorrect,
sinusoid: sinusoidQuestionWithDefaultCorrect,
tangent: tangentQuestionWithDefaultCorrect,
logarithm: logarithmQuestionWithDefaultCorrect,
"unlimited-point": pointQuestionWithDefaultCorrect,
"unlimited-polygon": polygonQuestionDefaultCorrect,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,23 @@ export const graphWithLabeledFunction: PerseusRenderer =
})
.build();

export const logarithmQuestion: PerseusRenderer =
interactiveGraphQuestionBuilder()
.withContent(
"**Graph $f(x) = \\log(x + 6)$ in the interactive widget.**\n\n[[☃ interactive-graph 1]]",
)
.withLogarithm({
coords: [
[-4, -3],
[-5, -7],
],
asymptote: -6,
})
.build();

export const logarithmQuestionWithDefaultCorrect: PerseusRenderer =
interactiveGraphQuestionBuilder().withLogarithm().build();

export const sinusoidWithPiTicks: PerseusRenderer =
interactiveGraphQuestionBuilder()
.withXRange(-6 * Math.PI, 6 * Math.PI)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,8 @@ const renderGraphElements = (props: {
return renderAbsoluteValueGraph(state, dispatch, i18n);
case "tangent":
return renderTangentGraph(state, dispatch, i18n);
case "logarithm":
return {graph: null, interactiveElementsDescription: null};
default:
throw new UnreachableCaseError(type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
SegmentGraphState,
SinusoidGraphState,
TangentGraphState,
LogarithmGraphState,
} from "./types";
import type {PerseusGraphType} from "@khanacademy/perseus-core";

Expand Down Expand Up @@ -491,4 +492,47 @@ describe("mafsStateToInteractiveGraph", () => {
],
});
});

it("converts the state of a logarithm graph", () => {
const graph: PerseusGraphType = {
type: "logarithm",
startCoords: {
coords: [
[5, 6],
[7, 8],
],
asymptote: 3,
},
};
const state: LogarithmGraphState = {
...commonGraphState,
type: "logarithm",
coords: [
[1, 2],
[3, 4],
],
asymptote: -1,
};

const result: PerseusGraphType = mafsStateToInteractiveGraph(
state,
graph,
);

expect(result).toEqual({
type: "logarithm",
coords: [
[1, 2],
[3, 4],
],
asymptote: -1,
startCoords: {
coords: [
[5, 6],
[7, 8],
],
asymptote: 3,
},
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ export function mafsStateToInteractiveGraph(
...originalGraph,
coords: state.coords,
};
case "logarithm":
invariant(originalGraph.type === "logarithm");
return {
...originalGraph,
coords: state.coords,
asymptote: state.asymptote,
};
default:
throw new UnreachableCaseError(state);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,72 @@ describe("initializeGraphState for exponential graphs", () => {
});
});

describe("initializeGraphState for logarithm graphs", () => {
it("uses the given coords and asymptote if present", () => {
// Arrange, Act
const graph = initializeGraphState({
...baseGraphData,
graph: {
type: "logarithm",
coords: [
[-4, -3],
[-5, -7],
],
asymptote: -6,
},
});

// Assert
invariant(graph.type === "logarithm");
expect(graph.coords).toEqual([
[-4, -3],
[-5, -7],
]);
expect(graph.asymptote).toBe(-6);
});

it("uses startCoords if given and explicit coords are absent", () => {
// Arrange, Act
const graph = initializeGraphState({
...baseGraphData,
graph: {
type: "logarithm",
startCoords: {
coords: [
[1, 4],
[3, 8],
],
asymptote: 2,
},
},
});

// Assert
invariant(graph.type === "logarithm");
expect(graph.coords).toEqual([
[1, 4],
[3, 8],
]);
expect(graph.asymptote).toBe(2);
});

it("uses default coords and asymptote if neither coords nor startCoords are given", () => {
// Arrange, Act
const graph = initializeGraphState({
...baseGraphData,
graph: {type: "logarithm"},
});

// Assert
invariant(graph.type === "logarithm");
expect(graph.coords).toEqual([
[1, 1],
[5, 5],
]);
expect(graph.asymptote).toBe(0);
});
});

describe("initializeGraphState for tangent graphs", () => {
it("uses the given coords, if present", () => {
const graph = initializeGraphState({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
PerseusGraphTypeSinusoid,
PerseusGraphTypeExponential,
PerseusGraphTypeTangent,
PerseusGraphTypeLogarithm,
} from "@khanacademy/perseus-core";
import type {Interval} from "mafs";

Expand Down Expand Up @@ -149,7 +150,8 @@ export function initializeGraphState(
case "logarithm":
return {
...shared,
type: "none",
type: graph.type,
...getLogarithmCoords(graph, range, step),
};
default:
throw new UnreachableCaseError(graph);
Expand Down Expand Up @@ -516,6 +518,37 @@ export function getExponentialCoords(
return {coords, asymptote};
}

export function getLogarithmCoords(
graph: PerseusGraphTypeLogarithm,
range: [x: Interval, y: Interval],
step: [x: number, y: number],
): {coords: [Coord, Coord]; asymptote: number} {
if (graph.coords && graph.asymptote != null) {
return {
coords: [graph.coords[0], graph.coords[1]],
asymptote: graph.asymptote,
};
}

// Default coords as normalized fractions of the graph range. After
// normalization with the default asymptote at x=0, both points will
// be to the right of the asymptote.
let defaultCoords: [Coord, Coord] = [
[0.55, 0.55],
[0.75, 0.75],
];
defaultCoords = normalizePoints(range, step, defaultCoords, true);

const coords: [Coord, Coord] = graph.startCoords
? graph.startCoords.coords
: defaultCoords;
const asymptote: number = graph.startCoords
? graph.startCoords.asymptote
: 0;

return {coords, asymptote};
}

export const getAngleCoords = (params: {
graph: PerseusGraphTypeAngle;
range: [x: Interval, y: Interval];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ export const actions = {
movePoint,
moveCenter,
},
logarithm: {
movePoint,
moveCenter,
},
absoluteValue: {
movePoint,
},
Expand Down
Loading
Loading