diff --git a/.changeset/heavy-deers-whisper.md b/.changeset/heavy-deers-whisper.md new file mode 100644 index 0000000000..f2487ad764 --- /dev/null +++ b/.changeset/heavy-deers-whisper.md @@ -0,0 +1,7 @@ +--- +"@khanacademy/perseus": minor +"@khanacademy/perseus-core": minor +"@khanacademy/perseus-editor": minor +--- + +Creation of initial types and stubs for Vector graph diff --git a/packages/perseus-core/src/data-schema.ts b/packages/perseus-core/src/data-schema.ts index 74bded5eb3..c4ab803f35 100644 --- a/packages/perseus-core/src/data-schema.ts +++ b/packages/perseus-core/src/data-schema.ts @@ -1110,7 +1110,8 @@ export type PerseusGraphType = | PerseusGraphTypeSinusoid | PerseusGraphTypeExponential | PerseusGraphTypeTangent - | PerseusGraphTypeLogarithm; + | PerseusGraphTypeLogarithm + | PerseusGraphTypeVector; export type PerseusGraphTypeAngle = { type: "angle"; @@ -1277,6 +1278,18 @@ export type PerseusGraphTypeRay = { startCoords?: CollinearTuple; }; +export type PerseusGraphTypeVector = { + type: "vector"; + /** The tail and tip coordinates of the vector: [tail, tip] */ + coords?: CollinearTuple | null; + /** The initial coordinates the graph renders with. */ + startCoords?: CollinearTuple; + /** How to match the answer. + * "exact" (default) — both tail and tip must match exactly. + * "congruent" — same direction and magnitude, any position. */ + match?: "exact" | "congruent"; +}; + type AbsoluteValueGraphCorrect = { type: "absolute-value"; coords: [Coord, Coord]; @@ -1357,6 +1370,15 @@ type RayGraphCorrect = { coords: CollinearTuple; }; +type VectorGraphCorrect = { + type: "vector"; + coords: CollinearTuple; + /** How to match the answer. + * "exact" (default) — both tail and tip must match exactly. + * "congruent" — same direction and magnitude, any position. */ + match?: "exact" | "congruent"; +}; + export type PerseusGraphCorrectType = | AbsoluteValueGraphCorrect | AngleGraphCorrect @@ -1372,7 +1394,8 @@ export type PerseusGraphCorrectType = | SinusoidGraphCorrect | ExponentialGraphCorrect | TangentGraphCorrect - | LogarithmGraphCorrect; + | LogarithmGraphCorrect + | VectorGraphCorrect; /** Options for the label-image widget. Asks learners to label image parts. */ export type PerseusLabelImageWidgetOptions = { diff --git a/packages/perseus-core/src/parse-perseus-json/perseus-parsers/interactive-graph-widget.ts b/packages/perseus-core/src/parse-perseus-json/perseus-parsers/interactive-graph-widget.ts index c45fc3a6c4..0aebb47313 100644 --- a/packages/perseus-core/src/parse-perseus-json/perseus-parsers/interactive-graph-widget.ts +++ b/packages/perseus-core/src/parse-perseus-json/perseus-parsers/interactive-graph-widget.ts @@ -149,6 +149,13 @@ const parsePerseusGraphTypeLogarithm = object({ ), }); +const parsePerseusGraphTypeVector = object({ + type: constant("vector"), + coords: optional(nullable(pair(pairOfNumbers, pairOfNumbers))), + startCoords: optional(pair(pairOfNumbers, pairOfNumbers)), + match: optional(enumeration("exact", "congruent")), +}); + export const parsePerseusGraphType = discriminatedUnionOn("type") .withBranch("absolute-value", parsePerseusGraphTypeAbsoluteValue) .withBranch("angle", parsePerseusGraphTypeAngle) @@ -164,7 +171,8 @@ export const parsePerseusGraphType = discriminatedUnionOn("type") .withBranch("segment", parsePerseusGraphTypeSegment) .withBranch("sinusoid", parsePerseusGraphTypeSinusoid) .withBranch("tangent", parsePerseusGraphTypeTangent) - .withBranch("logarithm", parsePerseusGraphTypeLogarithm).parser; + .withBranch("logarithm", parsePerseusGraphTypeLogarithm) + .withBranch("vector", parsePerseusGraphTypeVector).parser; const parseLockedFigureColor = enumeration(...lockedFigureColorNames); diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/interactive-graph-editor.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/interactive-graph-editor.tsx index a9345a86a7..81416f25f7 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/interactive-graph-editor.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/interactive-graph-editor.tsx @@ -573,6 +573,9 @@ function mergeGraphs( case "logarithm": invariant(b.type === "logarithm"); return {...a, ...b}; + case "vector": + invariant(b.type === "vector"); + return {...a, ...b}; default: throw new UnreachableCaseError(a); } diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/start-coords/util.ts b/packages/perseus-editor/src/widgets/interactive-graph-editor/start-coords/util.ts index c546efbd24..24adf65ca6 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/start-coords/util.ts +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/start-coords/util.ts @@ -300,6 +300,7 @@ export const shouldShowStartCoordsUI = ( case "sinusoid": case "absolute-value": case "logarithm": + case "vector": return true; default: throw new UnreachableCaseError(graph); diff --git a/packages/perseus/src/widget-ai-utils/interactive-graph/interactive-graph-ai-utils.ts b/packages/perseus/src/widget-ai-utils/interactive-graph/interactive-graph-ai-utils.ts index b7d32c2f08..a397e633bc 100644 --- a/packages/perseus/src/widget-ai-utils/interactive-graph/interactive-graph-ai-utils.ts +++ b/packages/perseus/src/widget-ai-utils/interactive-graph/interactive-graph-ai-utils.ts @@ -92,6 +92,11 @@ type LogarithmGraphOptions = { startCoords?: {coords: readonly [Coord, Coord]; asymptote: number}; }; +type VectorGraphOptions = { + type: "vector"; + startCoords?: CollinearTuple; +}; + type NoneGraphOptions = Record; type GraphOptions = @@ -109,7 +114,8 @@ type GraphOptions = | SegmentGraphOptions | SinusoidGraphOptions | TangentGraphOptions - | LogarithmGraphOptions; + | LogarithmGraphOptions + | VectorGraphOptions; type AngleUserInput = { coords?: readonly [Coord, Coord, Coord]; @@ -175,6 +181,10 @@ type TangentUserInput = { coords?: readonly Coord[] | null; }; +type VectorUserInput = { + coords?: CollinearTuple | null; +}; + type UserInput = | AbsoluteValueUserInput | AngleUserInput @@ -189,7 +199,8 @@ type UserInput = | SegmentUserInput | SinusoidUserInput | TangentUserInput - | LogarithmUserInput; + | LogarithmUserInput + | VectorUserInput; /** * JSON describing an interactive graph widget. Intended for consumption by AI tools. @@ -328,6 +339,11 @@ const getGraphOptionsForProps = ( type: props.userInput.type, startCoords: props.userInput.startCoords, }; + case "vector": + return { + type: props.userInput.type, + startCoords: props.userInput.startCoords, + }; default: throw new UnreachableCaseError(type); } @@ -399,6 +415,10 @@ const getUserInput = (userInput: PerseusGraphType): UserInput => { coords: userInput.coords, asymptote: userInput.asymptote, }; + case "vector": + return { + coords: userInput.coords, + }; default: throw new UnreachableCaseError(type); } diff --git a/packages/perseus/src/widgets/interactive-graphs/interactive-graph.tsx b/packages/perseus/src/widgets/interactive-graphs/interactive-graph.tsx index dcc58295a0..3be1aa92bd 100644 --- a/packages/perseus/src/widgets/interactive-graphs/interactive-graph.tsx +++ b/packages/perseus/src/widgets/interactive-graphs/interactive-graph.tsx @@ -616,6 +616,8 @@ class InteractiveGraph extends React.Component { return InteractiveGraph.getTangentEquationString(props); case "logarithm": return InteractiveGraph.getLogarithmEquationString(props); + case "vector": + return ""; default: throw new UnreachableCaseError(type); } diff --git a/packages/perseus/src/widgets/interactive-graphs/mafs-graph.tsx b/packages/perseus/src/widgets/interactive-graphs/mafs-graph.tsx index 528aaf7059..8532612a14 100644 --- a/packages/perseus/src/widgets/interactive-graphs/mafs-graph.tsx +++ b/packages/perseus/src/widgets/interactive-graphs/mafs-graph.tsx @@ -780,6 +780,8 @@ const renderGraphElements = (props: { return renderTangentGraph(state, dispatch, i18n); case "logarithm": return renderLogarithmGraph(state, dispatch, i18n); + case "vector": + throw new Error("Not implemented"); default: throw new UnreachableCaseError(type); } diff --git a/packages/perseus/src/widgets/interactive-graphs/mafs-state-to-interactive-graph.ts b/packages/perseus/src/widgets/interactive-graphs/mafs-state-to-interactive-graph.ts index b98e6cca8f..d1501992e4 100644 --- a/packages/perseus/src/widgets/interactive-graphs/mafs-state-to-interactive-graph.ts +++ b/packages/perseus/src/widgets/interactive-graphs/mafs-state-to-interactive-graph.ts @@ -117,6 +117,12 @@ export function mafsStateToInteractiveGraph( coords: state.coords, asymptote: state.asymptote, }; + case "vector": + invariant(originalGraph.type === "vector"); + return { + ...originalGraph, + coords: state.coords, + }; default: throw new UnreachableCaseError(state); } diff --git a/packages/perseus/src/widgets/interactive-graphs/reducer/initialize-graph-state.ts b/packages/perseus/src/widgets/interactive-graphs/reducer/initialize-graph-state.ts index 1f888d7d11..c7e0438240 100644 --- a/packages/perseus/src/widgets/interactive-graphs/reducer/initialize-graph-state.ts +++ b/packages/perseus/src/widgets/interactive-graphs/reducer/initialize-graph-state.ts @@ -153,6 +153,8 @@ export function initializeGraphState( type: graph.type, ...getLogarithmCoords(graph, range, step), }; + case "vector": + throw new Error("Not implemented"); default: throw new UnreachableCaseError(graph); } diff --git a/packages/perseus/src/widgets/interactive-graphs/reducer/interactive-graph-reducer.ts b/packages/perseus/src/widgets/interactive-graphs/reducer/interactive-graph-reducer.ts index c4dc75929e..c4ddcae7f2 100644 --- a/packages/perseus/src/widgets/interactive-graphs/reducer/interactive-graph-reducer.ts +++ b/packages/perseus/src/widgets/interactive-graphs/reducer/interactive-graph-reducer.ts @@ -316,6 +316,7 @@ function doMovePointInFigure( case "tangent": case "exponential": case "logarithm": + case "vector": throw new Error( `Don't use movePointInFigure for ${state.type} graphs. Use movePoint instead!`, ); diff --git a/packages/perseus/src/widgets/interactive-graphs/types.ts b/packages/perseus/src/widgets/interactive-graphs/types.ts index 6ec5c13d75..1feddddc87 100644 --- a/packages/perseus/src/widgets/interactive-graphs/types.ts +++ b/packages/perseus/src/widgets/interactive-graphs/types.ts @@ -43,7 +43,8 @@ export type InteractiveGraphState = | SinusoidGraphState | ExponentialGraphState | TangentGraphState - | LogarithmGraphState; + | LogarithmGraphState + | VectorGraphState; export type UnlimitedGraphState = PointGraphState | PolygonGraphState; @@ -91,6 +92,11 @@ export interface RayGraphState extends InteractiveGraphStateCommon { coords: PairOfPoints; } +interface VectorGraphState extends InteractiveGraphStateCommon { + type: "vector"; + coords: PairOfPoints; +} + export interface PolygonGraphState extends InteractiveGraphStateCommon { type: "polygon"; showAngles: boolean;