@khanacademy/perseus 24.3.0 → 25.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as KAS from '@khanacademy/kas';
2
2
  import { MathInputI18nContext, CursorContext, getKeyTranslator, createMathField, getCursorContext, DesktopKeypad, convertDotToTimesByLocale, mathQuillInstance, KeypadInput, KeypadType, KeyArray, keypadElementPropType } from '@khanacademy/math-input';
3
+ import { PerseusError, Errors, addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
3
4
  import * as PerseusLinter from '@khanacademy/perseus-linter';
4
5
  import { linterContextDefault, Rule, linterContextProps } from '@khanacademy/perseus-linter';
5
6
  import { View, RenderStateRoot, UniqueIDProvider, useLatestRef, useUniqueIdWithMock, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
@@ -39,7 +40,6 @@ import Switch from '@khanacademy/wonder-blocks-switch';
39
40
  import Pill from '@khanacademy/wonder-blocks-pill';
40
41
  import { Popper } from 'react-popper';
41
42
  import Link from '@khanacademy/wonder-blocks-link';
42
- import { addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
43
43
 
44
44
  function _extends() {
45
45
  _extends = Object.assign ? Object.assign.bind() : function (target) {
@@ -699,52 +699,9 @@ var dependencies = /*#__PURE__*/Object.freeze({
699
699
  // TODO(LP-11481): Change the primary API we use for logging in Perseus to use
700
700
  // getDependencies() internally... simplify usage to just using a "singleton"
701
701
  // which get's the injected logger.
702
- /**
703
- * @typedef {Object} Errors utility for referencing the Perseus error taxonomy.
704
- */
705
- const Errors = Object.freeze({
706
- /**
707
- * @property {ErrorKind} Unknown The kind of error is not known.
708
- */
709
- Unknown: "Unknown",
710
- /**
711
- * @property {ErrorKind} Internal The error is internal to the executing code.
712
- */
713
- Internal: "Internal",
714
- /**
715
- * @property {ErrorKind} InvalidInput There was a problem with the provided
716
- * input, such as the wrong format or a null value.
717
- */
718
- InvalidInput: "InvalidInput",
719
- /**
720
- * @property {ErrorKind} NotAllowed There was a problem due to the state of
721
- * the system not matching the requested operation or input. For example,
722
- * trying to create a username that is valid, but is already taken by
723
- * another user. Use {@link InvalidInput} instead when the input isn't
724
- * valid regardless of the state of the system. Use {@link NotFound} when
725
- * the failure is due to not being able to find a resource.
726
- */
727
- NotAllowed: "NotAllowed",
728
- /**
729
- * @property {ErrorKind} TransientService There was a problem when making a
730
- * request to a service.
731
- */
732
- TransientService: "TransientService",
733
- /**
734
- * @property {ErrorKind} Service There was a non-transient problem when
735
- * making a request to service.
736
- */
737
- Service: "Service"
738
- });
739
-
740
- /**
741
- * @type {ErrorKind} The kind of error being reported
742
- */
743
-
744
702
  /**
745
703
  * Provides logging infrastructure for Perseus
746
704
  */
747
-
748
705
  const Log = {
749
706
  // Logs a message
750
707
  log: (message, extra) => {
@@ -756,16 +713,6 @@ const Log = {
756
713
  }
757
714
  };
758
715
 
759
- class PerseusError extends Error {
760
- constructor(message, kind, options) {
761
- super(message);
762
- this.kind = void 0;
763
- this.metadata = void 0;
764
- this.kind = kind;
765
- this.metadata = options == null ? void 0 : options.metadata;
766
- }
767
- }
768
-
769
716
  /**
770
717
  * These are things that widgets should exclude when serializing themselves.
771
718
  *
@@ -28428,7 +28375,41 @@ function inset(amount, box) {
28428
28375
  return [trim(amount[X], box[X]), trim(amount[Y], box[Y])];
28429
28376
  }
28430
28377
 
28378
+ // [L]inear Int[erp]olation: gets the weighted average of two values `a` and
28379
+ // `b`, with the given `fraction` specifying how much weight `a` and `b` get:
28380
+ // - if `fraction` is 0, `lerp` returns `a`.
28381
+ // - if `fraction` is 0.5, `lerp` returns the average of `a` and `b`.
28382
+ // - if `fraction` is 1, `lerp` returns `b`.
28383
+ function lerp(a, b, fraction) {
28384
+ return (b - a) * clamp(fraction, 0, 1) + a;
28385
+ }
28386
+
28387
+ // Determines whether two line segments have exactly one intersection point.
28388
+ // The segments are treated as "open" - that is, not inclusive of their
28389
+ // endpoints.
28390
+ // Algorithm from https://stackoverflow.com/a/24392281/7347484
28391
+ const segmentsIntersect = ([[a, b], [c, d]], [[p, q], [r, s]]) => {
28392
+ const determinant = (c - a) * (s - q) - (r - p) * (d - b);
28393
+ if (determinant === 0) {
28394
+ return false;
28395
+ } else {
28396
+ const lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / determinant;
28397
+ const gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / determinant;
28398
+ return 0 < lambda && lambda < 1 && 0 < gamma && gamma < 1;
28399
+ }
28400
+ };
28401
+
28431
28402
  // This file contains helper functions for working with angles.
28403
+
28404
+ function convertDegreesToRadians(degrees) {
28405
+ return degrees / 180 * Math.PI;
28406
+ }
28407
+
28408
+ // Returns a value between -180 and 180, inclusive. The angle is measured
28409
+ // between the positive x-axis and the given vector.
28410
+ function calculateAngleInDegrees([x, y]) {
28411
+ return Math.atan2(y, x) * 180 / Math.PI;
28412
+ }
28432
28413
  function polar(r, th) {
28433
28414
  if (typeof r === "number") {
28434
28415
  r = [r, r];
@@ -28766,51 +28747,6 @@ function Arrowhead(props) {
28766
28747
  })));
28767
28748
  }
28768
28749
 
28769
- function calculateAngleInDegrees([x, y]) {
28770
- return Math.atan2(y, x) * 180 / Math.PI;
28771
- }
28772
-
28773
- /**
28774
- * Given a ray and a rectangular box, find the point where the ray intersects
28775
- * the edge of the box. Assumes the `initialPoint` is inside the box.
28776
- * @param initialPoint - The starting point of the ray.
28777
- * @param throughPoint - A point that the ray passes through. Must be different from initialPoint.
28778
- * @param box - The box with which to intersect the ray, in the form [[xMin, xMax], [yMin, yMax]]
28779
- */
28780
- const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
28781
- const [[xMin, xMax], [yMin, yMax]] = box;
28782
- const [aX, aY] = initialPoint;
28783
- const [bX, bY] = throughPoint;
28784
- const yDiff = bY - aY;
28785
- const xDiff = bX - aX;
28786
- const slope = yDiff / xDiff;
28787
- const inverseSlope = 1 / slope;
28788
- const xExtreme = xDiff < 0 ? xMin : xMax;
28789
- const yExtreme = yDiff < 0 ? yMin : yMax;
28790
- const yAtXExtreme = aY + (xExtreme - aX) * slope;
28791
- const xAtYExtreme = aX + (yExtreme - aY) * inverseSlope;
28792
- switch (true) {
28793
- // does the ray exit the graph bounding box via the left or right edge?
28794
- case isBetween(yAtXExtreme, yMin, yMax):
28795
- return [xExtreme, yAtXExtreme];
28796
-
28797
- // does the ray exit the graph bounding box via the top or bottom edge?
28798
- case isBetween(xAtYExtreme, xMin, xMax):
28799
- return [xAtYExtreme, yExtreme];
28800
- default:
28801
- return [xExtreme, yExtreme];
28802
- }
28803
- };
28804
- const getLines = points => {
28805
- return points.map((point, i) => {
28806
- const next = points[(i + 1) % points.length];
28807
- return [point, next];
28808
- });
28809
- };
28810
- function isBetween(x, low, high) {
28811
- return x >= low && x <= high;
28812
- }
28813
-
28814
28750
  function SVGLine(props) {
28815
28751
  const {
28816
28752
  start,
@@ -28856,6 +28792,41 @@ function Vector(props) {
28856
28792
  }));
28857
28793
  }
28858
28794
 
28795
+ /**
28796
+ * Given a ray and a rectangular box, find the point where the ray intersects
28797
+ * the edge of the box. Assumes the `initialPoint` is inside the box.
28798
+ * @param initialPoint - The starting point of the ray.
28799
+ * @param throughPoint - A point that the ray passes through. Must be different from initialPoint.
28800
+ * @param box - The box with which to intersect the ray, in the form [[xMin, xMax], [yMin, yMax]]
28801
+ */
28802
+ const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
28803
+ const [[xMin, xMax], [yMin, yMax]] = box;
28804
+ const [aX, aY] = initialPoint;
28805
+ const [bX, bY] = throughPoint;
28806
+ const yDiff = bY - aY;
28807
+ const xDiff = bX - aX;
28808
+ const slope = yDiff / xDiff;
28809
+ const inverseSlope = 1 / slope;
28810
+ const xExtreme = xDiff < 0 ? xMin : xMax;
28811
+ const yExtreme = yDiff < 0 ? yMin : yMax;
28812
+ const yAtXExtreme = aY + (xExtreme - aX) * slope;
28813
+ const xAtYExtreme = aX + (yExtreme - aY) * inverseSlope;
28814
+ switch (true) {
28815
+ // does the ray exit the graph bounding box via the left or right edge?
28816
+ case isBetween(yAtXExtreme, yMin, yMax):
28817
+ return [xExtreme, yAtXExtreme];
28818
+
28819
+ // does the ray exit the graph bounding box via the top or bottom edge?
28820
+ case isBetween(xAtYExtreme, xMin, xMax):
28821
+ return [xAtYExtreme, yExtreme];
28822
+ default:
28823
+ return [xExtreme, yExtreme];
28824
+ }
28825
+ };
28826
+ function isBetween(x, low, high) {
28827
+ return x >= low && x <= high;
28828
+ }
28829
+
28859
28830
  const LockedLine = props => {
28860
28831
  const {
28861
28832
  color: color$1,
@@ -29311,10 +29282,7 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
29311
29282
  const wbColorName = (_Object$entries$find$ = (_Object$entries$find = Object.entries(color).find(([_, value]) => value === color$1)) == null ? void 0 : _Object$entries$find[0]) != null ? _Object$entries$find$ : "blue";
29312
29283
  const pointClasses = `movable-point ${dragging ? "movable-point--dragging" : ""} ${focusClass(focusBehavior)}`;
29313
29284
  const [[x, y]] = useTransformVectorsToPixels(point);
29314
-
29315
- // TODO(benchristel): destructure range in one line
29316
- const [xMin, xMax] = range[X];
29317
- const [yMin, yMax] = range[Y];
29285
+ const [[xMin, xMax], [yMin, yMax]] = range;
29318
29286
  const [[verticalStartX]] = useTransformVectorsToPixels([xMin, 0]);
29319
29287
  const [[verticalEndX]] = useTransformVectorsToPixels([xMax, 0]);
29320
29288
  const [[_, horizontalStartY]] = useTransformVectorsToPixels([0, yMin]);
@@ -29893,24 +29861,11 @@ const shouldDrawArcOutside = (midpoint, vertex, range, polygonLines) => {
29893
29861
  // Create a ray from the midpoint (inside angle) to the edge of the range
29894
29862
  const rangeIntersectionPoint = getIntersectionOfRayWithBox(midpoint, vertex, range);
29895
29863
  let lineIntersectionCount = 0;
29896
- polygonLines.forEach(line => linesIntersect([vertex, rangeIntersectionPoint], line) && lineIntersectionCount++);
29864
+ polygonLines.forEach(line => segmentsIntersect([vertex, rangeIntersectionPoint], line) && lineIntersectionCount++);
29897
29865
 
29898
29866
  // If the number of intersections is even, the angle is inside the polygon
29899
29867
  return !isEven(lineIntersectionCount);
29900
29868
  };
29901
-
29902
- // https://stackoverflow.com/a/24392281/7347484
29903
- // The "intersects" function in geometry doesn't seem to work for this use case
29904
- const linesIntersect = ([[a, b], [c, d]], [[p, q], [r, s]]) => {
29905
- const determinant = (c - a) * (s - q) - (r - p) * (d - b);
29906
- if (determinant === 0) {
29907
- return false;
29908
- } else {
29909
- const lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / determinant;
29910
- const gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / determinant;
29911
- return 0 < lambda && lambda < 1 && 0 < gamma && gamma < 1;
29912
- }
29913
- };
29914
29869
  const isEven = n => n % 2 === 0;
29915
29870
 
29916
29871
  // This function calculates the bisector point of an angle formed by two points
@@ -30103,6 +30058,12 @@ const PolygonGraph = props => {
30103
30058
  onMove: destination => dispatch(movePoint(i, destination))
30104
30059
  })));
30105
30060
  };
30061
+ function getLines(points) {
30062
+ return points.map((point, i) => {
30063
+ const next = points[(i + 1) % points.length];
30064
+ return [point, next];
30065
+ });
30066
+ }
30106
30067
 
30107
30068
  function getGradableGraph(state, initialGraph) {
30108
30069
  if (!state.hasBeenInteractedWith) {
@@ -30479,12 +30440,8 @@ const AxisTickLabels = () => {
30479
30440
  tickStep,
30480
30441
  range
30481
30442
  } = graphConfig;
30482
-
30483
- // TODO(benchristel): use destructuring here
30484
- const [xMin, xMax] = range[X];
30485
- const [yMin, yMax] = range[Y];
30486
- const yTickStep = tickStep[Y];
30487
- const xTickStep = tickStep[X];
30443
+ const [[xMin, xMax], [yMin, yMax]] = range;
30444
+ const [xTickStep, yTickStep] = tickStep;
30488
30445
  const yGridTicks = generateTickLocations$1(yTickStep, yMin, yMax);
30489
30446
  const xGridTicks = generateTickLocations$1(xTickStep, xMin, xMax);
30490
30447
  return /*#__PURE__*/React.createElement("div", {
@@ -30864,12 +30821,8 @@ const AxisTicks = () => {
30864
30821
  width,
30865
30822
  height
30866
30823
  };
30867
-
30868
- // TODO(benchristel): destructure these in one line
30869
- const [xMin, xMax] = range[X];
30870
- const [yMin, yMax] = range[Y];
30871
- const yTickStep = tickStep[Y];
30872
- const xTickStep = tickStep[X];
30824
+ const [[xMin, xMax], [yMin, yMax]] = range;
30825
+ const [xTickStep, yTickStep] = tickStep;
30873
30826
  const yGridTicks = generateTickLocations(yTickStep, yMin, yMax);
30874
30827
  const xGridTicks = generateTickLocations(xTickStep, xMin, xMax);
30875
30828
  return /*#__PURE__*/React.createElement("g", {
@@ -31597,7 +31550,7 @@ function Protractor() {
31597
31550
  function RotationArrow() {
31598
31551
  const radius = 175;
31599
31552
  const angleDeg = 10;
31600
- const angleRad = degreesToRadians(angleDeg);
31553
+ const angleRad = convertDegreesToRadians(angleDeg);
31601
31554
  const endX = radius * (1 - Math.cos(angleRad));
31602
31555
  const endY = radius * -Math.sin(angleRad);
31603
31556
  const rotationArrow = pathBuilder().move(0, 0).circularArc(radius, endX, endY, {
@@ -31676,17 +31629,135 @@ function useDraggablePx(args) {
31676
31629
  });
31677
31630
  }
31678
31631
 
31679
- // [L]inear Int[erp]olation: gets the weighted average of two values `a` and
31680
- // `b`, with the given `fraction` specifying how much weight `a` and `b` get:
31681
- // - if `fraction` is 0, `lerp` returns `a`.
31682
- // - if `fraction` is 0.5, `lerp` returns the average of `a` and `b`.
31683
- // - if `fraction` is 1, `lerp` returns `b`.
31684
- function lerp(a, b, fraction) {
31685
- return (b - a) * fraction + a;
31686
- }
31687
- function degreesToRadians(degrees) {
31688
- return degrees / 180 * Math.PI;
31689
- }
31632
+ const MafsGraph = props => {
31633
+ const {
31634
+ state,
31635
+ dispatch,
31636
+ labels
31637
+ } = props;
31638
+ const [width, height] = props.box;
31639
+ const tickStep = props.step;
31640
+ return /*#__PURE__*/React.createElement(GraphConfigContext.Provider, {
31641
+ value: {
31642
+ range: state.range,
31643
+ snapStep: state.snapStep,
31644
+ markings: props.markings,
31645
+ tickStep: tickStep,
31646
+ gridStep: props.gridStep,
31647
+ showTooltips: !!props.showTooltips,
31648
+ graphDimensionsInPixels: props.box,
31649
+ width,
31650
+ height,
31651
+ labels
31652
+ }
31653
+ }, /*#__PURE__*/React.createElement(View, {
31654
+ className: "mafs-graph",
31655
+ style: {
31656
+ width,
31657
+ height,
31658
+ position: "relative",
31659
+ padding: "25px 25px 0 0",
31660
+ boxSizing: "content-box",
31661
+ marginLeft: "20px",
31662
+ marginBottom: "20px"
31663
+ }
31664
+ }, /*#__PURE__*/React.createElement(LegacyGrid, {
31665
+ box: props.box,
31666
+ backgroundImage: props.backgroundImage
31667
+ }), /*#__PURE__*/React.createElement(View, {
31668
+ style: {
31669
+ position: "absolute",
31670
+ bottom: 0,
31671
+ left: 0
31672
+ }
31673
+ }, props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisLabels, null), /*#__PURE__*/React.createElement(AxisTickLabels, null)), /*#__PURE__*/React.createElement(Mafs, {
31674
+ preserveAspectRatio: false,
31675
+ viewBox: {
31676
+ x: state.range[X],
31677
+ y: state.range[Y],
31678
+ padding: 0
31679
+ },
31680
+ pan: false,
31681
+ zoom: false,
31682
+ width: width,
31683
+ height: height
31684
+ }, /*#__PURE__*/React.createElement(SvgDefs, null), /*#__PURE__*/React.createElement(Grid, {
31685
+ tickStep: props.step,
31686
+ gridStep: props.gridStep,
31687
+ range: state.range,
31688
+ containerSizeClass: props.containerSizeClass,
31689
+ markings: props.markings
31690
+ }), props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
31691
+ lockedFigures: props.lockedFigures,
31692
+ range: state.range
31693
+ }), "/* Protractor */", props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
31694
+ state,
31695
+ dispatch
31696
+ })))));
31697
+ };
31698
+ const renderGraph = props => {
31699
+ const {
31700
+ state,
31701
+ dispatch
31702
+ } = props;
31703
+ const {
31704
+ type
31705
+ } = state;
31706
+ switch (type) {
31707
+ case "angle":
31708
+ return /*#__PURE__*/React.createElement(AngleGraph, {
31709
+ graphState: state,
31710
+ dispatch: dispatch
31711
+ });
31712
+ case "segment":
31713
+ return /*#__PURE__*/React.createElement(SegmentGraph, {
31714
+ graphState: state,
31715
+ dispatch: dispatch
31716
+ });
31717
+ case "linear-system":
31718
+ return /*#__PURE__*/React.createElement(LinearSystemGraph, {
31719
+ graphState: state,
31720
+ dispatch: dispatch
31721
+ });
31722
+ case "linear":
31723
+ return /*#__PURE__*/React.createElement(LinearGraph, {
31724
+ graphState: state,
31725
+ dispatch: dispatch
31726
+ });
31727
+ case "ray":
31728
+ return /*#__PURE__*/React.createElement(RayGraph, {
31729
+ graphState: state,
31730
+ dispatch: dispatch
31731
+ });
31732
+ case "polygon":
31733
+ return /*#__PURE__*/React.createElement(PolygonGraph, {
31734
+ graphState: state,
31735
+ dispatch: dispatch
31736
+ });
31737
+ case "point":
31738
+ return /*#__PURE__*/React.createElement(PointGraph, {
31739
+ graphState: state,
31740
+ dispatch: dispatch
31741
+ });
31742
+ case "circle":
31743
+ return /*#__PURE__*/React.createElement(CircleGraph, {
31744
+ graphState: state,
31745
+ dispatch: dispatch
31746
+ });
31747
+ case "quadratic":
31748
+ return /*#__PURE__*/React.createElement(QuadraticGraph, {
31749
+ graphState: state,
31750
+ dispatch: dispatch
31751
+ });
31752
+ case "sinusoid":
31753
+ return /*#__PURE__*/React.createElement(SinusoidGraph, {
31754
+ graphState: state,
31755
+ dispatch: dispatch
31756
+ });
31757
+ default:
31758
+ return new UnreachableCaseError(type);
31759
+ }
31760
+ };
31690
31761
 
31691
31762
  function initializeGraphState(params) {
31692
31763
  var _graph$snapTo;
@@ -31767,12 +31838,16 @@ function initializeGraphState(params) {
31767
31838
  }
31768
31839
  }
31769
31840
  function getPointCoords(graph, range, step) {
31770
- var _graph$coords;
31841
+ var _graph$coords, _graph$startCoords;
31771
31842
  const numPoints = graph.numPoints || 1;
31772
31843
  let coords = (_graph$coords = graph.coords) == null ? void 0 : _graph$coords.slice();
31773
31844
  if (coords) {
31774
31845
  return coords;
31775
31846
  }
31847
+ const startCoords = (_graph$startCoords = graph.startCoords) == null ? void 0 : _graph$startCoords.slice();
31848
+ if (startCoords) {
31849
+ return startCoords;
31850
+ }
31776
31851
  switch (numPoints) {
31777
31852
  case 1:
31778
31853
  // Back in the day, one point's coords were in graph.coord
@@ -31806,6 +31881,9 @@ function getSegmentCoords(graph, range, step) {
31806
31881
  if (graph.coords) {
31807
31882
  return graph.coords;
31808
31883
  }
31884
+ if (graph.startCoords) {
31885
+ return graph.startCoords;
31886
+ }
31809
31887
  const ys = n => {
31810
31888
  switch (n) {
31811
31889
  case 2:
@@ -31835,20 +31913,30 @@ function getLineCoords(graph, range, step) {
31835
31913
  if (graph.coords) {
31836
31914
  return graph.coords;
31837
31915
  }
31916
+ if (graph.startCoords) {
31917
+ return graph.startCoords;
31918
+ }
31838
31919
  return normalizePoints(range, step, defaultLinearCoords[0]);
31839
31920
  }
31840
31921
  function getLinearSystemCoords(graph, range, step) {
31841
31922
  if (graph.coords) {
31842
31923
  return graph.coords;
31843
31924
  }
31925
+ if (graph.startCoords) {
31926
+ return graph.startCoords;
31927
+ }
31844
31928
  return defaultLinearCoords.map(points => normalizePoints(range, step, points));
31845
31929
  }
31846
31930
  function getPolygonCoords(graph, range, step) {
31847
- var _graph$coords2;
31931
+ var _graph$coords2, _graph$startCoords2;
31848
31932
  let coords = (_graph$coords2 = graph.coords) == null ? void 0 : _graph$coords2.slice();
31849
31933
  if (coords) {
31850
31934
  return coords;
31851
31935
  }
31936
+ const startCoords = (_graph$startCoords2 = graph.startCoords) == null ? void 0 : _graph$startCoords2.slice();
31937
+ if (startCoords) {
31938
+ return startCoords;
31939
+ }
31852
31940
  const n = graph.numSides || 3;
31853
31941
  if (n === "unlimited") {
31854
31942
  coords = [];
@@ -31872,6 +31960,9 @@ function getSinusoidCoords(graph, range, step) {
31872
31960
  if (graph.coords) {
31873
31961
  return [graph.coords[0], graph.coords[1]];
31874
31962
  }
31963
+ if (graph.startCoords) {
31964
+ return [graph.startCoords[0], graph.startCoords[1]];
31965
+ }
31875
31966
  let coords = [[0.5, 0.5], [0.65, 0.6]];
31876
31967
  coords = normalizePoints(range, step, coords, true);
31877
31968
  return coords;
@@ -31880,6 +31971,9 @@ function getQuadraticCoords(graph, range, step) {
31880
31971
  if (graph.coords) {
31881
31972
  return graph.coords;
31882
31973
  }
31974
+ if (graph.startCoords) {
31975
+ return graph.startCoords;
31976
+ }
31883
31977
  const defaultCoords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
31884
31978
  return normalizePoints(range, step, defaultCoords, true);
31885
31979
  }
@@ -31890,6 +31984,12 @@ function getCircleCoords(graph) {
31890
31984
  radiusPoint: vec.add(graph.center, [graph.radius, 0])
31891
31985
  };
31892
31986
  }
31987
+ if (graph.startCoords) {
31988
+ return {
31989
+ center: graph.startCoords,
31990
+ radiusPoint: vec.add(graph.startCoords, [2, 0])
31991
+ };
31992
+ }
31893
31993
  return {
31894
31994
  center: [0, 0],
31895
31995
  radiusPoint: [2, 0]
@@ -32604,70 +32704,6 @@ function setAtIndex(args) {
32604
32704
  return copy;
32605
32705
  }
32606
32706
 
32607
- const renderGraph = props => {
32608
- const {
32609
- state,
32610
- dispatch
32611
- } = props;
32612
- const {
32613
- type
32614
- } = state;
32615
- switch (type) {
32616
- case "angle":
32617
- return /*#__PURE__*/React.createElement(AngleGraph, {
32618
- graphState: state,
32619
- dispatch: dispatch
32620
- });
32621
- case "segment":
32622
- return /*#__PURE__*/React.createElement(SegmentGraph, {
32623
- graphState: state,
32624
- dispatch: dispatch
32625
- });
32626
- case "linear-system":
32627
- return /*#__PURE__*/React.createElement(LinearSystemGraph, {
32628
- graphState: state,
32629
- dispatch: dispatch
32630
- });
32631
- case "linear":
32632
- return /*#__PURE__*/React.createElement(LinearGraph, {
32633
- graphState: state,
32634
- dispatch: dispatch
32635
- });
32636
- case "ray":
32637
- return /*#__PURE__*/React.createElement(RayGraph, {
32638
- graphState: state,
32639
- dispatch: dispatch
32640
- });
32641
- case "polygon":
32642
- return /*#__PURE__*/React.createElement(PolygonGraph, {
32643
- graphState: state,
32644
- dispatch: dispatch
32645
- });
32646
- case "point":
32647
- return /*#__PURE__*/React.createElement(PointGraph, {
32648
- graphState: state,
32649
- dispatch: dispatch
32650
- });
32651
- case "circle":
32652
- return /*#__PURE__*/React.createElement(CircleGraph, {
32653
- graphState: state,
32654
- dispatch: dispatch
32655
- });
32656
- case "quadratic":
32657
- return /*#__PURE__*/React.createElement(QuadraticGraph, {
32658
- graphState: state,
32659
- dispatch: dispatch
32660
- });
32661
- case "sinusoid":
32662
- return /*#__PURE__*/React.createElement(SinusoidGraph, {
32663
- graphState: state,
32664
- dispatch: dispatch
32665
- });
32666
- default:
32667
- return new UnreachableCaseError(type);
32668
- }
32669
- };
32670
-
32671
32707
  // Rather than be tightly bound to how data was structured in
32672
32708
  // the legacy interactive graph, this lets us store state
32673
32709
  // however we want and we just transform it before handing it off
@@ -32683,6 +32719,7 @@ function mafsStateToInteractiveGraph(state) {
32683
32719
  return _extends({}, state);
32684
32720
  }
32685
32721
  const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
32722
+ var _graph$startCoords;
32686
32723
  const {
32687
32724
  onChange,
32688
32725
  graph
@@ -32717,6 +32754,7 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
32717
32754
  const snapTo = graph.type === "polygon" ? graph.snapTo : null;
32718
32755
  const showAngles = graph.type === "polygon" ? graph.showAngles : null;
32719
32756
  const showSides = graph.type === "polygon" ? graph.showSides : null;
32757
+ const startCoords = (_graph$startCoords = graph.startCoords) != null ? _graph$startCoords : null;
32720
32758
  const originalPropsRef = useRef(props);
32721
32759
  const latestPropsRef = useLatestRef(props);
32722
32760
  useEffect(() => {
@@ -32727,78 +32765,12 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
32727
32765
  if (latestPropsRef.current !== originalPropsRef.current) {
32728
32766
  dispatch(reinitialize(latestPropsRef.current));
32729
32767
  }
32730
- }, [graph.type, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef]);
32768
+ }, [graph.type, numSegments, numSides, snapTo, showAngles, showSides, startCoords, latestPropsRef, startCoords]);
32731
32769
  return /*#__PURE__*/React.createElement(MafsGraph, _extends({}, props, {
32732
32770
  state: state,
32733
32771
  dispatch: dispatch
32734
32772
  }));
32735
32773
  });
32736
- const MafsGraph = props => {
32737
- const {
32738
- state,
32739
- dispatch,
32740
- labels
32741
- } = props;
32742
- const [width, height] = props.box;
32743
- const tickStep = props.step;
32744
- return /*#__PURE__*/React.createElement(GraphConfigContext.Provider, {
32745
- value: {
32746
- range: state.range,
32747
- snapStep: state.snapStep,
32748
- markings: props.markings,
32749
- tickStep: tickStep,
32750
- gridStep: props.gridStep,
32751
- showTooltips: !!props.showTooltips,
32752
- graphDimensionsInPixels: props.box,
32753
- width,
32754
- height,
32755
- labels
32756
- }
32757
- }, /*#__PURE__*/React.createElement(View, {
32758
- className: "mafs-graph",
32759
- style: {
32760
- width,
32761
- height,
32762
- position: "relative",
32763
- padding: "25px 25px 0 0",
32764
- boxSizing: "content-box",
32765
- marginLeft: "20px",
32766
- marginBottom: "20px"
32767
- }
32768
- }, /*#__PURE__*/React.createElement(LegacyGrid, {
32769
- box: props.box,
32770
- backgroundImage: props.backgroundImage
32771
- }), /*#__PURE__*/React.createElement(View, {
32772
- style: {
32773
- position: "absolute",
32774
- bottom: 0,
32775
- left: 0
32776
- }
32777
- }, props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisLabels, null), /*#__PURE__*/React.createElement(AxisTickLabels, null)), /*#__PURE__*/React.createElement(Mafs, {
32778
- preserveAspectRatio: false,
32779
- viewBox: {
32780
- x: state.range[X],
32781
- y: state.range[Y],
32782
- padding: 0
32783
- },
32784
- pan: false,
32785
- zoom: false,
32786
- width: width,
32787
- height: height
32788
- }, /*#__PURE__*/React.createElement(SvgDefs, null), /*#__PURE__*/React.createElement(Grid, {
32789
- tickStep: props.step,
32790
- gridStep: props.gridStep,
32791
- range: state.range,
32792
- containerSizeClass: props.containerSizeClass,
32793
- markings: props.markings
32794
- }), props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
32795
- lockedFigures: props.lockedFigures,
32796
- range: state.range
32797
- }), "/* Protractor */", props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
32798
- state,
32799
- dispatch
32800
- })))));
32801
- };
32802
32774
 
32803
32775
  const {
32804
32776
  DeprecationMixin
@@ -41865,6 +41837,7 @@ const rules = {
41865
41837
  }, /*#__PURE__*/React.createElement("span", {
41866
41838
  style: SQUARE_LABEL_STYLE
41867
41839
  }, node.content)), /*#__PURE__*/React.createElement(AltText, {
41840
+ key: "alt-text",
41868
41841
  id: "questionMarker",
41869
41842
  number: node.content
41870
41843
  }), node.space ? "\u00A0" : null];
@@ -41889,6 +41862,7 @@ const rules = {
41889
41862
  }, /*#__PURE__*/React.createElement("span", {
41890
41863
  style: CIRCLE_LABEL_STYLE
41891
41864
  }, node.content)), /*#__PURE__*/React.createElement(AltText, {
41865
+ key: "alt-text",
41892
41866
  id: "circleMarker",
41893
41867
  number: node.content
41894
41868
  }), node.space ? "\u00A0" : null];
@@ -41912,6 +41886,7 @@ const rules = {
41912
41886
  className: "perseus-passage-bracket-label",
41913
41887
  "aria-hidden": "true"
41914
41888
  }, "[", node.content, "]"), /*#__PURE__*/React.createElement(AltText, {
41889
+ key: "alt-text",
41915
41890
  id: "sentenceMarker",
41916
41891
  number: node.content
41917
41892
  }), node.space ? "\u00A0" : null];
@@ -44920,7 +44895,7 @@ const init = function init(options) {
44920
44895
 
44921
44896
  // This file is processed by a Rollup plugin (replace) to inject the production
44922
44897
  const libName = "@khanacademy/perseus";
44923
- const libVersion = "24.3.0";
44898
+ const libVersion = "25.1.0";
44924
44899
  addLibraryVersionToPerseusDebug(libName, libVersion);
44925
44900
 
44926
44901
  const apiVersion = {
@@ -47921,5 +47896,5 @@ const EditorJsonify = {
47921
47896
  }
47922
47897
  };
47923
47898
 
47924
- export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAnswerFromUserInput, getAnswersFromWidgets, getCorrectAnswerForWidgetId, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
47899
+ export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAnswerFromUserInput, getAnswersFromWidgets, getCorrectAnswerForWidgetId, getLineCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
47925
47900
  //# sourceMappingURL=index.js.map