@khanacademy/perseus 21.1.0 → 21.2.1

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/index.css CHANGED
@@ -4284,6 +4284,7 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
4284
4284
  --movable-point-ring-radius: calc(2px + var(--movable-point-center-radius));
4285
4285
  --movable-point-halo-radius: calc(3px + var(--movable-point-ring-radius));
4286
4286
  --movable-point-hover-expansion: 2px;
4287
+ --movable-point-focus-ring-offset: 2px;
4287
4288
 
4288
4289
  --movable-line-stroke-color: var(--mafs-blue);
4289
4290
  --movable-line-stroke-weight: 2px;
@@ -4305,7 +4306,13 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
4305
4306
  fill: transparent;
4306
4307
  }
4307
4308
 
4308
- .MafsView :is(.movable-point-center, .movable-point-ring, .movable-point-halo) {
4309
+ .MafsView
4310
+ :is(
4311
+ .movable-point-center,
4312
+ .movable-point-ring,
4313
+ .movable-point-halo,
4314
+ .movable-point-focus-outline
4315
+ ) {
4309
4316
  transition: r 0.15s ease-out;
4310
4317
  }
4311
4318
 
@@ -4344,7 +4351,24 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
4344
4351
  );
4345
4352
  }
4346
4353
 
4347
- .MafsView .movable-point:focus-visible .movable-point-halo {
4348
- outline: 2px solid blue;
4349
- border-radius: 99px;
4354
+ .MafsView .movable-point .movable-point-focus-outline {
4355
+ visibility: hidden;
4356
+ r: calc(
4357
+ var(--movable-point-halo-radius) +
4358
+ var(--movable-point-focus-ring-offset)
4359
+ );
4360
+ stroke-width: 2px;
4361
+ fill: none;
4362
+ stroke: var(--mafs-blue);
4363
+ }
4364
+
4365
+ .MafsView .movable-point:hover .movable-point-focus-outline {
4366
+ r: calc(
4367
+ var(--movable-point-hover-expansion) + var(--movable-point-halo-radius) +
4368
+ var(--movable-point-focus-ring-offset)
4369
+ );
4370
+ }
4371
+
4372
+ .MafsView .movable-point:focus-visible .movable-point-focus-outline {
4373
+ visibility: visible;
4350
4374
  }
package/dist/index.js CHANGED
@@ -3281,6 +3281,15 @@ const textarea = {
3281
3281
  */
3282
3282
  const unescapeMathMode$1 = label => label.startsWith("$") && label.endsWith("$") ? label.slice(1, -1) : label;
3283
3283
  const random$1 = seededRNG$1(new Date().getTime() & 0xffffffff);
3284
+
3285
+ // TODO(benchristel): in the future, we may want to make deepClone work for
3286
+ // Record<string, Cloneable> as well. Currently, it only does arrays.
3287
+ function deepClone(obj) {
3288
+ if (Array.isArray(obj)) {
3289
+ return obj.map(deepClone);
3290
+ }
3291
+ return obj;
3292
+ }
3284
3293
  const Util = {
3285
3294
  inputPathsEqual,
3286
3295
  nestedMap: nestedMap$2,
@@ -3326,7 +3335,8 @@ const Util = {
3326
3335
  getDataUrl: getDataUrl,
3327
3336
  textarea,
3328
3337
  unescapeMathMode: unescapeMathMode$1,
3329
- random: random$1
3338
+ random: random$1,
3339
+ deepClone
3330
3340
  };
3331
3341
 
3332
3342
  // Derived from the MIT-licensed:
@@ -28364,6 +28374,28 @@ function movePoint(index, destination) {
28364
28374
  destination
28365
28375
  };
28366
28376
  }
28377
+ const CHANGE_SNAP_STEP = "change-snap-step";
28378
+ function changeSnapStep(snapStep) {
28379
+ return {
28380
+ type: CHANGE_SNAP_STEP,
28381
+ snapStep
28382
+ };
28383
+ }
28384
+ const CHANGE_RANGE = "change-range";
28385
+ function changeRange(range) {
28386
+ return {
28387
+ type: CHANGE_RANGE,
28388
+ range
28389
+ };
28390
+ }
28391
+
28392
+ const GraphStateContext = /*#__PURE__*/React.createContext({
28393
+ state: {},
28394
+ dispatch: () => {}
28395
+ });
28396
+ function useGraphState() {
28397
+ return React__default["default"].useContext(GraphStateContext);
28398
+ }
28367
28399
 
28368
28400
  /**
28369
28401
  * 44 is touch best practice and AAA compliant for WCAG
@@ -28390,6 +28422,11 @@ const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.
28390
28422
  const extent = ranges[i][1] - ranges[i][0];
28391
28423
  return (coord + ranges[i][1]) / extent;
28392
28424
  }));
28425
+ function snap(snapStep, point) {
28426
+ const [requestedX, requestedY] = point;
28427
+ const [snapX, snapY] = snapStep;
28428
+ return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
28429
+ }
28393
28430
 
28394
28431
  const useTransform = function () {
28395
28432
  const {
@@ -28448,6 +28485,9 @@ const getLines = points => points.map((point, i) => {
28448
28485
 
28449
28486
  const defaultStroke = "var(--movable-line-stroke-color)";
28450
28487
  const MovableLine = props => {
28488
+ const {
28489
+ state
28490
+ } = useGraphState();
28451
28491
  const {
28452
28492
  start,
28453
28493
  end,
@@ -28472,7 +28512,7 @@ const MovableLine = props => {
28472
28512
  onMove: newPoint => {
28473
28513
  onMove(mafs.vec.sub(newPoint, midpoint));
28474
28514
  },
28475
- constrain: p => p
28515
+ constrain: p => snap(state.snapStep, p)
28476
28516
  });
28477
28517
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("g", {
28478
28518
  ref: line,
@@ -28523,7 +28563,13 @@ function SVGLine(props) {
28523
28563
  });
28524
28564
  }
28525
28565
 
28566
+ // The hitbox size of 48px by 48px is preserved from the legacy interactive
28567
+ // graph.
28568
+ const hitboxSizePx = 48;
28526
28569
  const StyledMovablePoint = props => {
28570
+ const {
28571
+ state
28572
+ } = useGraphState();
28527
28573
  const hitboxRef = React.useRef(null);
28528
28574
  const {
28529
28575
  point,
@@ -28536,7 +28582,7 @@ const StyledMovablePoint = props => {
28536
28582
  gestureTarget: hitboxRef,
28537
28583
  point,
28538
28584
  onMove,
28539
- constrain: p => p
28585
+ constrain: p => snap(state.snapStep, p)
28540
28586
  });
28541
28587
  const [[x, y]] = useTransform(point);
28542
28588
  return /*#__PURE__*/React__namespace.createElement("g", {
@@ -28551,7 +28597,7 @@ const StyledMovablePoint = props => {
28551
28597
  }
28552
28598
  }, /*#__PURE__*/React__namespace.createElement("circle", {
28553
28599
  className: "movable-point-hitbox",
28554
- r: TARGET_SIZE / 2,
28600
+ r: hitboxSizePx / 2,
28555
28601
  cx: x,
28556
28602
  cy: y
28557
28603
  }), /*#__PURE__*/React__namespace.createElement("circle", {
@@ -28562,6 +28608,10 @@ const StyledMovablePoint = props => {
28562
28608
  className: "movable-point-ring",
28563
28609
  cx: x,
28564
28610
  cy: y
28611
+ }), /*#__PURE__*/React__namespace.createElement("circle", {
28612
+ className: "movable-point-focus-outline",
28613
+ cx: x,
28614
+ cy: y
28565
28615
  }), /*#__PURE__*/React__namespace.createElement("circle", {
28566
28616
  className: "movable-point-center",
28567
28617
  cx: x,
@@ -28887,7 +28937,8 @@ const PolygonGraph = props => {
28887
28937
  type,
28888
28938
  showAngles,
28889
28939
  showSides,
28890
- range
28940
+ range,
28941
+ snapStep
28891
28942
  } = props.graphState;
28892
28943
  const points = coords ?? [[0, 0]];
28893
28944
  const pointsSum = points.reduce((acc, point) => mafs.vec.add(acc, point), [0, 0]);
@@ -28902,7 +28953,7 @@ const PolygonGraph = props => {
28902
28953
  const delta = mafs.vec.sub(newPoint, midpoint);
28903
28954
  dispatch(moveAll(delta));
28904
28955
  },
28905
- constrain: p => p
28956
+ constrain: p => snap(snapStep, p)
28906
28957
  });
28907
28958
  const active = hovered || focused || dragging;
28908
28959
  const lines = getLines(points);
@@ -28971,14 +29022,6 @@ function PointGraph(props) {
28971
29022
  })));
28972
29023
  }
28973
29024
 
28974
- const GraphStateContext = /*#__PURE__*/React.createContext({
28975
- state: {},
28976
- dispatch: () => {}
28977
- });
28978
- function useGraphState() {
28979
- return React__default["default"].useContext(GraphStateContext);
28980
- }
28981
-
28982
29025
  function Arrow(props) {
28983
29026
  const {
28984
29027
  userTransform,
@@ -29022,20 +29065,122 @@ function AxisArrows() {
29022
29065
  }));
29023
29066
  }
29024
29067
 
29025
- const renderLineLabel = (n, _ref) => {
29026
- let [min, max] = _ref;
29027
- return n !== -1 && n !== min && n !== max;
29068
+ const tickSize = 10;
29069
+ const tickStyle = {
29070
+ stroke: "black",
29071
+ strokeWidth: 1
29072
+ };
29073
+ const YGridTick = _ref => {
29074
+ let {
29075
+ y
29076
+ } = _ref;
29077
+ const pointOnAxis = [0, y];
29078
+ const [[xPosition, yPosition]] = useTransform(pointOnAxis);
29079
+ return /*#__PURE__*/React__namespace.createElement("g", {
29080
+ className: "y-axis-ticks"
29081
+ }, /*#__PURE__*/React__namespace.createElement("line", {
29082
+ x1: xPosition - tickSize / 2,
29083
+ y1: yPosition,
29084
+ x2: xPosition + tickSize / 2,
29085
+ y2: yPosition,
29086
+ style: tickStyle
29087
+ }));
29088
+ };
29089
+ const XGridTick = _ref2 => {
29090
+ let {
29091
+ x
29092
+ } = _ref2;
29093
+ const pointOnAxis = [x, 0];
29094
+ const [[xPosition, yPosition]] = useTransform(pointOnAxis);
29095
+ return /*#__PURE__*/React__namespace.createElement("g", {
29096
+ className: "x-axis-ticks"
29097
+ }, /*#__PURE__*/React__namespace.createElement("line", {
29098
+ x1: xPosition,
29099
+ y1: yPosition + tickSize / 2,
29100
+ x2: xPosition,
29101
+ y2: yPosition - tickSize / 2,
29102
+ style: tickStyle
29103
+ }));
29104
+ };
29105
+ function generateTickLocations(tickStep, min, max) {
29106
+ const ticks = [];
29107
+ for (let i = 0 + tickStep; i < max; i += tickStep) {
29108
+ ticks.push(i);
29109
+ }
29110
+ for (let i = 0 - tickStep; i > min; i -= tickStep) {
29111
+ ticks.push(i);
29112
+ }
29113
+ return ticks;
29114
+ }
29115
+ const AxisTicks = props => {
29116
+ const range = props.range;
29117
+ const [xMin, xMax] = range[0];
29118
+ const [yMin, yMax] = range[1];
29119
+ const yTickStep = props.tickStep[1];
29120
+ const xTickStep = props.tickStep[0];
29121
+ const yGridTicks = generateTickLocations(yTickStep, yMin, yMax);
29122
+ const xGridTicks = generateTickLocations(xTickStep, xMin, xMax);
29123
+ return /*#__PURE__*/React__namespace.createElement("g", {
29124
+ className: "axis-ticks"
29125
+ }, yGridTicks.map(y => {
29126
+ return /*#__PURE__*/React__namespace.createElement(YGridTick, {
29127
+ y: y,
29128
+ key: `y-grid-tick-${y}`
29129
+ });
29130
+ }), xGridTicks.map(x => {
29131
+ return /*#__PURE__*/React__namespace.createElement(XGridTick, {
29132
+ x: x,
29133
+ key: `x-grid-tick-${x}`
29134
+ });
29135
+ }));
29136
+ };
29137
+
29138
+ /**
29139
+ * lineLabelText get the text that should
29140
+ * be displayed for a specific tick place
29141
+ *
29142
+ * @param {number} n the tick number
29143
+ * @param {number} tickStep how frequently a label should appear
29144
+ * @param {vec.Vector2} range the min/max range on the axis
29145
+ */
29146
+ const lineLabelText = (n, tickStep, range) => {
29147
+ const [min, max] = range;
29148
+ const isOnStep = n % tickStep === 0;
29149
+ const isNegativeOne = n === -1;
29150
+ const isMin = n === min;
29151
+ const isMax = n === max;
29152
+ const shouldRender = isOnStep && !isNegativeOne && !isMin && !isMax;
29153
+ return shouldRender ? `${n}` : "";
29154
+ };
29155
+
29156
+ /**
29157
+ * axisOptions determine axis options for Mafs
29158
+ *
29159
+ * axisIndex is for grabbing data in an array that contains
29160
+ * data for multiple axes. For example range: [[-10, 10], [-10, 10]]
29161
+ * range[0] is data for the x axis and range[1] is data for the y axis
29162
+ *
29163
+ * @param {GridProps} props
29164
+ * @param {number} axisIndex which axis we're getting options for
29165
+ */
29166
+ const axisOptions = (props, axisIndex) => {
29167
+ const axisStep = props.tickStep[axisIndex];
29168
+ const axisRange = props.range[axisIndex];
29169
+ return {
29170
+ axis: props.markings === "graph",
29171
+ lines: props.gridStep[axisIndex],
29172
+ labels: n => lineLabelText(n, axisStep, axisRange)
29173
+ };
29174
+ };
29175
+ const Grid = props => {
29176
+ return props.markings === "none" ? null : /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Coordinates.Cartesian, {
29177
+ xAxis: axisOptions(props, 0),
29178
+ yAxis: axisOptions(props, 1)
29179
+ }), /*#__PURE__*/React__namespace.createElement(AxisTicks, {
29180
+ range: props.range,
29181
+ tickStep: props.tickStep
29182
+ }), props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(AxisArrows, null));
29028
29183
  };
29029
- const axisOptions = (props, index) => ({
29030
- axis: props.markings === "graph",
29031
- lines: props.step[index],
29032
- subdivisions: props.step[index] / props.gridStep[index],
29033
- labels: n => renderLineLabel(n, props.range[index]) ? n : ""
29034
- });
29035
- const Grid = props => props.markings === "none" ? null : /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Coordinates.Cartesian, {
29036
- xAxis: axisOptions(props, 0),
29037
- yAxis: axisOptions(props, 1)
29038
- }), props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(AxisArrows, null));
29039
29184
 
29040
29185
  // Displays a stylized open eye: lint warnings are visible
29041
29186
  const VisibleIcon = () => /*#__PURE__*/React__namespace.createElement("svg", {
@@ -29743,6 +29888,10 @@ function interactiveGraphReducer(state, action) {
29743
29888
  return doMoveAll(state, action);
29744
29889
  case MOVE_POINT:
29745
29890
  return doMovePoint(state, action);
29891
+ case CHANGE_SNAP_STEP:
29892
+ return doChangeSnapStep(state, action);
29893
+ case CHANGE_RANGE:
29894
+ return doChangeRange(state, action);
29746
29895
  default:
29747
29896
  throw new wonderStuffCore.UnreachableCaseError(action);
29748
29897
  }
@@ -29764,14 +29913,11 @@ function doMoveControlPoint(state, action) {
29764
29913
  update: tuple => setAtIndex({
29765
29914
  array: tuple,
29766
29915
  index: action.pointIndex,
29767
- newValue: snap({
29916
+ newValue: snap(snapStep, bound$1({
29768
29917
  snapStep,
29769
- point: bound$1({
29770
- snapStep,
29771
- range,
29772
- point: action.destination
29773
- })
29774
- })
29918
+ range,
29919
+ point: action.destination
29920
+ }))
29775
29921
  })
29776
29922
  });
29777
29923
  const coordsToCheck = newCoords[action.itemIndex];
@@ -29815,13 +29961,7 @@ function doMoveLine(state, action) {
29815
29961
  snapStep,
29816
29962
  range
29817
29963
  });
29818
- const newLine = [snap({
29819
- snapStep,
29820
- point: mafs.vec.add(currentLine[0], change)
29821
- }), snap({
29822
- snapStep,
29823
- point: mafs.vec.add(currentLine[1], change)
29824
- })];
29964
+ const newLine = [snap(snapStep, mafs.vec.add(currentLine[0], change)), snap(snapStep, mafs.vec.add(currentLine[1], change))];
29825
29965
  const newCoords = setAtIndex({
29826
29966
  array: state.coords,
29827
29967
  index: action.itemIndex,
@@ -29852,10 +29992,7 @@ function doMoveAll(state, action) {
29852
29992
  snapStep,
29853
29993
  range
29854
29994
  });
29855
- const newCoords = state.coords.map(point => snap({
29856
- snapStep,
29857
- point: mafs.vec.add(point, change)
29858
- }));
29995
+ const newCoords = state.coords.map(point => snap(snapStep, mafs.vec.add(point, change)));
29859
29996
  return {
29860
29997
  ...state,
29861
29998
  hasBeenInteractedWith: true,
@@ -29878,14 +30015,11 @@ function doMovePoint(state, action) {
29878
30015
  coords: setAtIndex({
29879
30016
  array: state.coords,
29880
30017
  index: action.index,
29881
- newValue: snap({
30018
+ newValue: snap(state.snapStep, bound$1({
29882
30019
  snapStep: state.snapStep,
29883
- point: bound$1({
29884
- snapStep: state.snapStep,
29885
- range: state.range,
29886
- point: action.destination
29887
- })
29888
- })
30020
+ range: state.range,
30021
+ point: action.destination
30022
+ }))
29889
30023
  })
29890
30024
  };
29891
30025
  }
@@ -29893,6 +30027,28 @@ function doMovePoint(state, action) {
29893
30027
  throw new Error("The movePoint action is only for point and polygon graphs");
29894
30028
  }
29895
30029
  }
30030
+ function doChangeSnapStep(state, action) {
30031
+ if (
30032
+ // Deep equality check since these are arrays
30033
+ ___default["default"].isEqual(state.snapStep, action.snapStep)) {
30034
+ return state;
30035
+ }
30036
+ return {
30037
+ ...state,
30038
+ snapStep: action.snapStep
30039
+ };
30040
+ }
30041
+ function doChangeRange(state, action) {
30042
+ if (
30043
+ // Deep equality check since these are arrays
30044
+ ___default["default"].isEqual(state.range, action.range)) {
30045
+ return state;
30046
+ }
30047
+ return {
30048
+ ...state,
30049
+ range: action.range
30050
+ };
30051
+ }
29896
30052
  const getChange = (coords, delta, constraintOpts) => {
29897
30053
  const [deltaX, deltaY] = delta;
29898
30054
  const maxMoves = coords.map(point => maxMove({
@@ -29911,24 +30067,14 @@ const getChange = (coords, delta, constraintOpts) => {
29911
30067
  const dy = clamp(deltaY, minYMove, maxYMove);
29912
30068
  return [dx, dy];
29913
30069
  };
29914
- function snap(_ref) {
29915
- let {
29916
- snapStep,
29917
- point
29918
- } = _ref;
29919
- const [requestedX, requestedY] = point;
29920
- const [snapX, snapY] = snapStep;
29921
- return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
29922
- }
29923
-
29924
30070
  // Returns the closest point to the given `point` that is within the graph
29925
30071
  // bounds given in `state`.
29926
- function bound$1(_ref2) {
30072
+ function bound$1(_ref) {
29927
30073
  let {
29928
30074
  snapStep,
29929
30075
  range,
29930
30076
  point
29931
- } = _ref2;
30077
+ } = _ref;
29932
30078
  const [requestedX, requestedY] = point;
29933
30079
  const [snapX, snapY] = snapStep;
29934
30080
  const [[minX, maxX], [minY, maxY]] = range;
@@ -29936,12 +30082,12 @@ function bound$1(_ref2) {
29936
30082
  }
29937
30083
 
29938
30084
  // Returns the vector from the given point to the top-right corner of the graph
29939
- function maxMove(_ref3) {
30085
+ function maxMove(_ref2) {
29940
30086
  let {
29941
30087
  snapStep,
29942
30088
  range,
29943
30089
  point
29944
- } = _ref3;
30090
+ } = _ref2;
29945
30091
  const topRight = bound$1({
29946
30092
  snapStep,
29947
30093
  range,
@@ -29952,12 +30098,12 @@ function maxMove(_ref3) {
29952
30098
 
29953
30099
  // Returns the vector from the given point to the bottom-left corner of the
29954
30100
  // graph
29955
- function minMove(_ref4) {
30101
+ function minMove(_ref3) {
29956
30102
  let {
29957
30103
  snapStep,
29958
30104
  range,
29959
30105
  point
29960
- } = _ref4;
30106
+ } = _ref3;
29961
30107
  const bottomLeft = bound$1({
29962
30108
  snapStep,
29963
30109
  range,
@@ -30282,6 +30428,18 @@ const MafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
30282
30428
  }
30283
30429
  prevState.current = state;
30284
30430
  }, [props, state]);
30431
+
30432
+ // Destructuring first to keep useEffect from making excess calls
30433
+ const [xSnap, ySnap] = props.snapStep;
30434
+ React.useEffect(() => {
30435
+ dispatch(changeSnapStep([xSnap, ySnap]));
30436
+ }, [xSnap, ySnap]);
30437
+
30438
+ // Destructuring first to keep useEffect from making excess calls
30439
+ const [[xMinRange, xMaxRange], [yMinRange, yMaxRange]] = props.range;
30440
+ React.useEffect(() => {
30441
+ dispatch(changeRange([[xMinRange, xMaxRange], [yMinRange, yMaxRange]]));
30442
+ }, [xMinRange, xMaxRange, yMinRange, yMaxRange]);
30285
30443
  React__namespace.useImperativeHandle(ref, () => ({
30286
30444
  getUserInput: () => getGradableGraph(state, props.graph)
30287
30445
  }));
@@ -30316,7 +30474,13 @@ const MafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
30316
30474
  zoom: false,
30317
30475
  width: width,
30318
30476
  height: height
30319
- }, /*#__PURE__*/React__namespace.createElement(SvgDefs, null), /*#__PURE__*/React__namespace.createElement(Grid, props), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
30477
+ }, /*#__PURE__*/React__namespace.createElement(SvgDefs, null), /*#__PURE__*/React__namespace.createElement(Grid, {
30478
+ tickStep: props.step,
30479
+ gridStep: props.gridStep,
30480
+ range: props.range,
30481
+ containerSizeClass: props.containerSizeClass,
30482
+ markings: props.markings
30483
+ }), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
30320
30484
  lockedFigures: props.lockedFigures
30321
30485
  }), renderGraph({
30322
30486
  state,
@@ -32066,8 +32230,8 @@ class InteractiveGraph extends React__namespace.Component {
32066
32230
  };
32067
32231
  }
32068
32232
  } else if (userInput.type === "segment" && rubric.correct.type === "segment" && userInput.coords != null) {
32069
- let guess = userInput.coords.slice();
32070
- let correct = rubric.correct.coords?.slice();
32233
+ let guess = Util.deepClone(userInput.coords);
32234
+ let correct = Util.deepClone(rubric.correct?.coords);
32071
32235
  guess = ___default["default"].invoke(guess, "sort").sort();
32072
32236
  // @ts-expect-error - TS2345 - Argument of type '(readonly Coord[])[] | undefined' is not assignable to parameter of type 'Collection<any>'.
32073
32237
  correct = ___default["default"].invoke(correct, "sort").sort();
@@ -42329,7 +42493,7 @@ const init = function (options) {
42329
42493
 
42330
42494
  // This file is processed by a Rollup plugin (replace) to inject the production
42331
42495
  const libName = "@khanacademy/perseus";
42332
- const libVersion = "21.1.0";
42496
+ const libVersion = "21.2.1";
42333
42497
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
42334
42498
 
42335
42499
  const apiVersion = {
@@ -43458,6 +43622,9 @@ const ItemExtras = [
43458
43622
  // A background image applied to various widgets.
43459
43623
 
43460
43624
  const PerseusExpressionAnswerFormConsidered = ["correct", "wrong", "ungraded"];
43625
+
43626
+ // 2D range: xMin, xMax, yMin, yMax
43627
+
43461
43628
  const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
43462
43629
 
43463
43630
  /**