@khanacademy/perseus 23.0.0 → 23.3.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/index.js CHANGED
@@ -10928,6 +10928,33 @@ function intersects(ab, cd) {
10928
10928
  }
10929
10929
  return false;
10930
10930
  }
10931
+
10932
+ // Whether any two sides of a polygon intersect each other
10933
+ function polygonSidesIntersect(vertices) {
10934
+ for (let i = 0; i < vertices.length; i++) {
10935
+ for (let k = i + 1; k < vertices.length; k++) {
10936
+ // If any two vertices are the same point, sides overlap
10937
+ if (kmath.point.equal(vertices[i], vertices[k])) {
10938
+ return true;
10939
+ }
10940
+
10941
+ // Find the other end of the sides starting at vertices i and k
10942
+ const iNext = (i + 1) % vertices.length;
10943
+ const kNext = (k + 1) % vertices.length;
10944
+
10945
+ // Adjacent sides always intersect (at the vertex); skip those
10946
+ if (iNext === k || kNext === i) {
10947
+ continue;
10948
+ }
10949
+ const side1 = [vertices[i], vertices[iNext]];
10950
+ const side2 = [vertices[k], vertices[kNext]];
10951
+ if (intersects(side1, side2)) {
10952
+ return true;
10953
+ }
10954
+ }
10955
+ }
10956
+ return false;
10957
+ }
10931
10958
  function vector(a, b) {
10932
10959
  return ___default["default"].map(___default["default"].zip(a, b), function (pair) {
10933
10960
  return pair[0] - pair[1];
@@ -17598,16 +17625,8 @@ class SvgImage extends React__namespace.Component {
17598
17625
  // Props to send to all images
17599
17626
  const imageProps = {
17600
17627
  alt: this.props.alt,
17601
- title: this.props.title,
17602
- tabIndex: "0"
17628
+ title: this.props.title
17603
17629
  };
17604
- // If alt text is present, AND overrideAriaHidden is true,
17605
- // include aria-hidden to avoid screen reader from crawling texts
17606
- // both from the div meant for screen readers (see svg-image.jsx)
17607
- // and alt attribute.
17608
- if (this.props.alt && this.props.overrideAriaHidden) {
17609
- imageProps["aria-hidden"] = true;
17610
- }
17611
17630
  const width = this.props.width && this.props.width * this.props.scale;
17612
17631
  const height = this.props.height && this.props.height * this.props.scale;
17613
17632
  const dimensions = {
@@ -27649,7 +27668,7 @@ class ImageWidget extends React__namespace.Component {
27649
27668
 
27650
27669
  render() {
27651
27670
  let image;
27652
- let alt;
27671
+ const alt = this.props.caption === this.props.alt ? "" : this.props.alt;
27653
27672
  const {
27654
27673
  apiOptions
27655
27674
  } = this.props;
@@ -27662,29 +27681,7 @@ class ImageWidget extends React__namespace.Component {
27662
27681
  } = _ref;
27663
27682
  return /*#__PURE__*/React__namespace.createElement(SvgImage, {
27664
27683
  src: url,
27665
- alt:
27666
- /* alt text is formatted in a sr-only
27667
- div next to the image in addition to
27668
- the alt attribute.
27669
- If there is no alt text at all,
27670
- we don't put an alt attribute on
27671
- the image, so that screen readers
27672
- know there's something they can't
27673
- read there :(.
27674
- NOTE: React <=0.13 (maybe later)
27675
- has a bug where it won't ever
27676
- remove an attribute, so if this
27677
- alt node is ever defined it's
27678
- not removed. This is sort of
27679
- dangerous, but we usually re-key
27680
- new renderers so that they're
27681
- rendered from scratch anyways,
27682
- so this shouldn't be a problem
27683
- in practice right now, although
27684
- it will exhibit weird behaviour
27685
- while editing. */
27686
- this.props.alt,
27687
- overrideAriaHidden: true,
27684
+ alt: alt,
27688
27685
  width: backgroundImage.width,
27689
27686
  height: backgroundImage.height,
27690
27687
  preloader: apiOptions.imagePreloader,
@@ -27701,16 +27698,6 @@ class ImageWidget extends React__namespace.Component {
27701
27698
  });
27702
27699
  });
27703
27700
  }
27704
- if (this.props.alt) {
27705
- alt = /*#__PURE__*/React__namespace.createElement("span", {
27706
- className: "perseus-sr-only"
27707
- }, /*#__PURE__*/React__namespace.createElement(Renderer, {
27708
- content: this.props.alt,
27709
- apiOptions: apiOptions,
27710
- linterContext: this.props.linterContext,
27711
- strings: this.context.strings
27712
- }));
27713
- }
27714
27701
 
27715
27702
  // For mobile we combine an image's title and caption.
27716
27703
  if (apiOptions.isMobile) {
@@ -27777,7 +27764,7 @@ class ImageWidget extends React__namespace.Component {
27777
27764
  style: {
27778
27765
  maxWidth: backgroundImage.width
27779
27766
  }
27780
- }, image, alt, titleAndCaption);
27767
+ }, image, titleAndCaption);
27781
27768
  }
27782
27769
  let title;
27783
27770
  let caption;
@@ -27809,7 +27796,7 @@ class ImageWidget extends React__namespace.Component {
27809
27796
  style: {
27810
27797
  maxWidth: backgroundImage.width
27811
27798
  }
27812
- }, title, image, alt, caption);
27799
+ }, title, image, caption);
27813
27800
  }
27814
27801
  static validate(userInput, rubric) {
27815
27802
  return {
@@ -28962,8 +28949,32 @@ const lockedFigureColors = {
28962
28949
  pink: "#B25071",
28963
28950
  red: "#D92916"
28964
28951
  };
28952
+ const lockedEllipseFillStyles = {
28953
+ none: 0,
28954
+ solid: 1,
28955
+ translucent: 0.4
28956
+ };
28965
28957
  const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
28966
28958
 
28959
+ const LockedEllipse = props => {
28960
+ const {
28961
+ center,
28962
+ radius,
28963
+ angle,
28964
+ color,
28965
+ fillStyle,
28966
+ strokeStyle
28967
+ } = props;
28968
+ return /*#__PURE__*/React__namespace.createElement(mafs.Ellipse, {
28969
+ center: center,
28970
+ radius: radius,
28971
+ angle: angle,
28972
+ fillOpacity: lockedEllipseFillStyles[fillStyle],
28973
+ strokeStyle: strokeStyle,
28974
+ color: lockedFigureColors[color]
28975
+ });
28976
+ };
28977
+
28967
28978
  function pathBuilder() {
28968
28979
  return new PathBuilder();
28969
28980
  }
@@ -29217,6 +29228,21 @@ const LockedPoint = props => {
29217
29228
  });
29218
29229
  };
29219
29230
 
29231
+ const LockedVector = props => {
29232
+ const {
29233
+ color,
29234
+ points
29235
+ } = props;
29236
+ const [tail, tip] = points;
29237
+ return /*#__PURE__*/React__namespace.createElement("g", {
29238
+ className: "locked-vector"
29239
+ }, /*#__PURE__*/React__namespace.createElement(Vector, {
29240
+ tail: tail,
29241
+ tip: tip,
29242
+ color: lockedFigureColors[color]
29243
+ }));
29244
+ };
29245
+
29220
29246
  const GraphLockedLayer = props => {
29221
29247
  const {
29222
29248
  lockedFigures
@@ -29232,6 +29258,14 @@ const GraphLockedLayer = props => {
29232
29258
  key: `line-${index}`,
29233
29259
  range: props.range
29234
29260
  }, figure));
29261
+ case "ellipse":
29262
+ return /*#__PURE__*/React__namespace.createElement(LockedEllipse, _extends({
29263
+ key: `ellipse-${index}`
29264
+ }, figure));
29265
+ case "vector":
29266
+ return /*#__PURE__*/React__namespace.createElement(LockedVector, _extends({
29267
+ key: `vector-${index}`
29268
+ }, figure));
29235
29269
  default:
29236
29270
  /**
29237
29271
  * Devlopment-time future-proofing: This should
@@ -29359,7 +29393,8 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
29359
29393
  point,
29360
29394
  color = wonderBlocksTokens.color.blue,
29361
29395
  dragging,
29362
- focusBehavior
29396
+ focusBehavior,
29397
+ cursor
29363
29398
  } = props;
29364
29399
 
29365
29400
  // WB Tooltip requires a color name for the background color.
@@ -29394,7 +29429,8 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
29394
29429
  ref: hitboxRef,
29395
29430
  className: pointClasses,
29396
29431
  style: {
29397
- "--movable-point-color": color
29432
+ "--movable-point-color": color,
29433
+ cursor
29398
29434
  },
29399
29435
  "data-testid": "movable-point",
29400
29436
  tabIndex: tabIndex(focusBehavior)
@@ -29642,7 +29678,33 @@ const SegmentGraph = props => {
29642
29678
  })));
29643
29679
  };
29644
29680
 
29645
- const LinearGraph = props => {
29681
+ const LinearGraph = (props, key) => {
29682
+ const {
29683
+ dispatch
29684
+ } = props;
29685
+ const {
29686
+ coords: line
29687
+ } = props.graphState;
29688
+
29689
+ // Linear graphs only have one line
29690
+ // (LEMS-2050): Update the reducer so that we have a separate action for moving one line
29691
+ // and another action for moving multiple lines
29692
+ return /*#__PURE__*/React__namespace.createElement(MovableLine, {
29693
+ key: 0,
29694
+ points: line,
29695
+ onMoveLine: delta => {
29696
+ dispatch(moveLine(0, delta));
29697
+ },
29698
+ extend: {
29699
+ start: true,
29700
+ end: true
29701
+ },
29702
+ onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, 0)),
29703
+ color: "var(--movable-line-stroke-color)"
29704
+ });
29705
+ };
29706
+
29707
+ const LinearSystemGraph = props => {
29646
29708
  const {
29647
29709
  dispatch
29648
29710
  } = props;
@@ -29662,7 +29724,7 @@ const LinearGraph = props => {
29662
29724
  },
29663
29725
  onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i)),
29664
29726
  color: colors[i]
29665
- })));
29727
+ })), ";");
29666
29728
  };
29667
29729
 
29668
29730
  const RayGraph = props => {
@@ -29670,14 +29732,14 @@ const RayGraph = props => {
29670
29732
  dispatch
29671
29733
  } = props;
29672
29734
  const {
29673
- coords: lines
29735
+ coords: line
29674
29736
  } = props.graphState;
29675
29737
  const handleMoveLine = delta => dispatch(moveLine(0, delta));
29676
29738
  const handleMovePoint = (pointIndex, newPoint) => dispatch(moveControlPoint(pointIndex, newPoint, 0));
29677
- return /*#__PURE__*/React__namespace.createElement(MovableLine
29678
- // a ray only has one line
29679
- , {
29680
- points: lines[0],
29739
+
29740
+ // Ray graphs only have one line
29741
+ return /*#__PURE__*/React__namespace.createElement(MovableLine, {
29742
+ points: line,
29681
29743
  onMoveLine: handleMoveLine,
29682
29744
  onMovePoint: handleMovePoint,
29683
29745
  extend: {
@@ -29864,6 +29926,7 @@ const StyledMovablePoint = props => {
29864
29926
  const {
29865
29927
  point,
29866
29928
  onMove,
29929
+ cursor,
29867
29930
  color = wonderBlocksTokens.color.blue
29868
29931
  } = props;
29869
29932
  const {
@@ -29882,7 +29945,8 @@ const StyledMovablePoint = props => {
29882
29945
  focusBehavior: {
29883
29946
  type: "uncontrolled",
29884
29947
  tabIndex: 0
29885
- }
29948
+ },
29949
+ cursor: cursor
29886
29950
  });
29887
29951
  };
29888
29952
 
@@ -29988,26 +30052,42 @@ function initializeGraphState(params) {
29988
30052
  return {
29989
30053
  ...shared,
29990
30054
  type: "segment",
29991
- coords: getDefaultSegments({
30055
+ coords: getSegmentCoords({
29992
30056
  graph,
29993
30057
  step,
29994
30058
  range
29995
30059
  })
29996
30060
  };
29997
30061
  case "linear":
29998
- case "linear-system":
30062
+ return {
30063
+ ...shared,
30064
+ type: graph.type,
30065
+ coords: getLineCoords({
30066
+ graph,
30067
+ range,
30068
+ step
30069
+ })
30070
+ };
29999
30071
  case "ray":
30000
30072
  return {
30001
30073
  ...shared,
30002
30074
  type: graph.type,
30003
- // Linear and ray graphs have a single tuple of points, while a
30004
- // linear system has two tuples of points.
30005
30075
  coords: getLineCoords({
30006
30076
  graph,
30007
30077
  range,
30008
30078
  step
30009
30079
  })
30010
30080
  };
30081
+ case "linear-system":
30082
+ return {
30083
+ ...shared,
30084
+ type: graph.type,
30085
+ coords: getLinearSystemCoords({
30086
+ graph,
30087
+ range,
30088
+ step
30089
+ })
30090
+ };
30011
30091
  case "polygon":
30012
30092
  return {
30013
30093
  ...shared,
@@ -30034,8 +30114,7 @@ function initializeGraphState(params) {
30034
30114
  return {
30035
30115
  ...shared,
30036
30116
  type: graph.type,
30037
- center: [0, 0],
30038
- radiusPoint: [1, 0]
30117
+ ...getCircleCoords(graph)
30039
30118
  };
30040
30119
  case "quadratic":
30041
30120
  return {
@@ -30043,8 +30122,13 @@ function initializeGraphState(params) {
30043
30122
  type: graph.type,
30044
30123
  coords: getQuadraticCoords(graph, range, step)
30045
30124
  };
30046
- case "angle":
30047
30125
  case "sinusoid":
30126
+ return {
30127
+ ...shared,
30128
+ type: graph.type,
30129
+ coords: getSinusoidCoords(graph, range, step)
30130
+ };
30131
+ case "angle":
30048
30132
  throw new Error("Mafs not yet implemented for graph type: " + graph.type);
30049
30133
  default:
30050
30134
  throw new wonderStuffCore.UnreachableCaseError(graph);
@@ -30114,13 +30198,13 @@ function getGradableGraph(state, initialGraph) {
30114
30198
  if (state.type === "linear" && initialGraph.type === "linear") {
30115
30199
  return {
30116
30200
  ...initialGraph,
30117
- coords: state.coords[0]
30201
+ coords: state.coords
30118
30202
  };
30119
30203
  }
30120
30204
  if (state.type === "ray" && initialGraph.type === "ray") {
30121
30205
  return {
30122
30206
  ...initialGraph,
30123
- coords: state.coords[0]
30207
+ coords: state.coords
30124
30208
  };
30125
30209
  }
30126
30210
  if (state.type === "polygon" && initialGraph.type === "polygon") {
@@ -30148,14 +30232,23 @@ function getGradableGraph(state, initialGraph) {
30148
30232
  coords: state.coords
30149
30233
  };
30150
30234
  }
30235
+ if (state.type === "sinusoid" && initialGraph.type === "sinusoid") {
30236
+ return {
30237
+ ...initialGraph,
30238
+ coords: state.coords
30239
+ };
30240
+ }
30151
30241
  throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
30152
30242
  }
30153
- const getDefaultSegments = _ref2 => {
30243
+ const getSegmentCoords = _ref2 => {
30154
30244
  let {
30155
30245
  graph,
30156
30246
  range,
30157
30247
  step
30158
30248
  } = _ref2;
30249
+ if (graph.coords) {
30250
+ return graph.coords;
30251
+ }
30159
30252
  const ys = n => {
30160
30253
  switch (n) {
30161
30254
  case 2:
@@ -30187,23 +30280,28 @@ const getLineCoords = _ref3 => {
30187
30280
  range,
30188
30281
  step
30189
30282
  } = _ref3;
30190
- // Return two lines for a linear system, one for a ray or linear
30191
- switch (graph.type) {
30192
- case "linear-system":
30193
- return defaultLinearCoords.map(points => normalizePoints(range, step, points));
30194
- case "linear":
30195
- case "ray":
30196
- return [normalizePoints(range, step, defaultLinearCoords[0])];
30197
- default:
30198
- throw new wonderStuffCore.UnreachableCaseError(graph);
30283
+ if (graph.coords) {
30284
+ return graph.coords;
30199
30285
  }
30286
+ return normalizePoints(range, step, defaultLinearCoords[0]);
30200
30287
  };
30201
- const getPolygonCoords = _ref4 => {
30288
+ const getLinearSystemCoords = _ref4 => {
30202
30289
  let {
30203
30290
  graph,
30204
30291
  range,
30205
30292
  step
30206
30293
  } = _ref4;
30294
+ if (graph.coords) {
30295
+ return graph.coords;
30296
+ }
30297
+ return defaultLinearCoords.map(points => normalizePoints(range, step, points));
30298
+ };
30299
+ const getPolygonCoords = _ref5 => {
30300
+ let {
30301
+ graph,
30302
+ range,
30303
+ step
30304
+ } = _ref5;
30207
30305
  let coords = graph.coords?.slice();
30208
30306
  if (coords) {
30209
30307
  return coords;
@@ -30227,11 +30325,33 @@ const getPolygonCoords = _ref4 => {
30227
30325
  coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
30228
30326
  return coords;
30229
30327
  };
30230
- const getQuadraticCoords = (graph, range, step) => {
30231
- let coords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
30328
+ const getSinusoidCoords = (graph, range, step) => {
30329
+ if (graph.coords) {
30330
+ return [graph.coords[0], graph.coords[1]];
30331
+ }
30332
+ let coords = [[0.5, 0.5], [0.65, 0.6]];
30232
30333
  coords = normalizePoints(range, step, coords, true);
30233
30334
  return coords;
30234
30335
  };
30336
+ const getQuadraticCoords = (graph, range, step) => {
30337
+ if (graph.coords) {
30338
+ return graph.coords;
30339
+ }
30340
+ const defaultCoords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
30341
+ return normalizePoints(range, step, defaultCoords, true);
30342
+ };
30343
+ const getCircleCoords = (graph, range, step) => {
30344
+ if (graph.center != null && graph.radius != null) {
30345
+ return {
30346
+ center: graph.center,
30347
+ radiusPoint: mafs.vec.add(graph.center, [graph.radius, 0])
30348
+ };
30349
+ }
30350
+ return {
30351
+ center: [0, 0],
30352
+ radiusPoint: [1, 0]
30353
+ };
30354
+ };
30235
30355
 
30236
30356
  /**
30237
30357
  * determine radius of a circle graph
@@ -30260,6 +30380,7 @@ function CircleGraph(props) {
30260
30380
  onMove: newCenter => dispatch(moveCenter(newCenter))
30261
30381
  }), /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
30262
30382
  point: radiusPoint,
30383
+ cursor: "ew-resize",
30263
30384
  onMove: newRadiusPoint => {
30264
30385
  dispatch(moveRadiusPoint(newRadiusPoint));
30265
30386
  }
@@ -30417,6 +30538,62 @@ const getQuadraticCoefficients = coords => {
30417
30538
  return [a, b, c];
30418
30539
  };
30419
30540
 
30541
+ function SinusoidGraph(props) {
30542
+ const {
30543
+ dispatch,
30544
+ graphState
30545
+ } = props;
30546
+
30547
+ // Destructure the coordinates from the graph state
30548
+ // Note: The order of the coordinates is important:
30549
+ // The coords[0] is the root and the coords[1] is the first peak
30550
+ const {
30551
+ coords
30552
+ } = graphState;
30553
+
30554
+ // Get the coefficients for calculating the quadratic equation
30555
+ const coeffs = getSinusoidCoefficients(coords);
30556
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Plot.OfX, {
30557
+ y: x => computeSine(x, coeffs),
30558
+ color: wonderBlocksTokens.color.blue
30559
+ }), coords.map((coord, i) => /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
30560
+ key: "point-" + i,
30561
+ point: coord,
30562
+ onMove: destination => dispatch(movePoint(i, destination))
30563
+ })));
30564
+ }
30565
+
30566
+ // Plot a sinusoid of the form: f(x) = a * sin(b * x - c) + d
30567
+ const computeSine = function (x,
30568
+ // x-coordinate
30569
+ sinusoidCoefficients) {
30570
+ // Break down the coefficients for the sine function to improve readability
30571
+ const {
30572
+ amplitude: a,
30573
+ angularFrequency: b,
30574
+ phase: c,
30575
+ verticalOffset: d
30576
+ } = sinusoidCoefficients;
30577
+ return a * Math.sin(b * x - c) + d;
30578
+ };
30579
+ const getSinusoidCoefficients = coords => {
30580
+ // It's assumed that p1 is the root and p2 is the first peak
30581
+ const p1 = coords[0];
30582
+ const p2 = coords[1];
30583
+
30584
+ // Resulting coefficients are canonical for this sine curve
30585
+ const amplitude = p2[1] - p1[1];
30586
+ const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
30587
+ const phase = p1[0] * angularFrequency;
30588
+ const verticalOffset = p1[1];
30589
+ return {
30590
+ amplitude,
30591
+ angularFrequency,
30592
+ phase,
30593
+ verticalOffset
30594
+ };
30595
+ };
30596
+
30420
30597
  // Universal padding for the axis ticks to ensure that the labels
30421
30598
  // have enough space to render without overlapping the tick lines
30422
30599
  const tickPadding = 5;
@@ -31512,15 +31689,9 @@ function interactiveGraphReducer(state, action) {
31512
31689
  }
31513
31690
  }
31514
31691
  function doMoveControlPoint(state, action) {
31515
- const {
31516
- snapStep,
31517
- range
31518
- } = state;
31519
31692
  switch (state.type) {
31520
31693
  case "segment":
31521
- case "linear":
31522
31694
  case "linear-system":
31523
- case "ray":
31524
31695
  {
31525
31696
  const newCoords = updateAtIndex({
31526
31697
  array: state.coords,
@@ -31528,11 +31699,7 @@ function doMoveControlPoint(state, action) {
31528
31699
  update: tuple => setAtIndex({
31529
31700
  array: tuple,
31530
31701
  index: action.pointIndex,
31531
- newValue: snap(snapStep, bound$1({
31532
- snapStep,
31533
- range,
31534
- point: action.destination
31535
- }))
31702
+ newValue: boundAndSnapToGrid(action.destination, state)
31536
31703
  })
31537
31704
  });
31538
31705
  const coordsToCheck = newCoords[action.itemIndex];
@@ -31545,16 +31712,34 @@ function doMoveControlPoint(state, action) {
31545
31712
  coords: newCoords
31546
31713
  };
31547
31714
  }
31715
+ case "linear":
31716
+ case "ray":
31717
+ {
31718
+ const newCoords = setAtIndex({
31719
+ array: state.coords,
31720
+ index: action.pointIndex,
31721
+ newValue: boundAndSnapToGrid(action.destination, state)
31722
+ });
31723
+ return {
31724
+ ...state,
31725
+ hasBeenInteractedWith: true,
31726
+ coords: newCoords
31727
+ };
31728
+ }
31548
31729
  case "circle":
31549
31730
  throw new Error("FIXME implement circle reducer");
31550
31731
  case "point":
31551
31732
  case "polygon":
31552
31733
  case "quadratic":
31734
+ case "sinusoid":
31553
31735
  throw new Error(`Don't use moveControlPoint for ${state.type} graphs. Use movePoint instead!`);
31554
31736
  default:
31555
31737
  throw new wonderStuffCore.UnreachableCaseError(state);
31556
31738
  }
31557
31739
  }
31740
+
31741
+ // (LEMS-2050): Update the reducer so that we have a separate action for moving one line
31742
+ // and another action for moving multiple lines
31558
31743
  function doMoveLine(state, action) {
31559
31744
  const {
31560
31745
  snapStep,
@@ -31562,9 +31747,7 @@ function doMoveLine(state, action) {
31562
31747
  } = state;
31563
31748
  switch (state.type) {
31564
31749
  case "segment":
31565
- case "linear":
31566
31750
  case "linear-system":
31567
- case "ray":
31568
31751
  {
31569
31752
  if (action.itemIndex === undefined) {
31570
31753
  throw new Error("Please provide index of line to move");
@@ -31590,6 +31773,22 @@ function doMoveLine(state, action) {
31590
31773
  coords: newCoords
31591
31774
  };
31592
31775
  }
31776
+ case "linear":
31777
+ case "ray":
31778
+ {
31779
+ const currentLine = state.coords;
31780
+ const change = getChange(currentLine, action.delta, {
31781
+ snapStep,
31782
+ range
31783
+ });
31784
+ const newLine = [snap(snapStep, mafs.vec.add(currentLine[0], change)), snap(snapStep, mafs.vec.add(currentLine[1], change))];
31785
+ return {
31786
+ ...state,
31787
+ type: state.type,
31788
+ hasBeenInteractedWith: true,
31789
+ coords: newLine
31790
+ };
31791
+ }
31593
31792
  default:
31594
31793
  // The MoveLine action doesn't make sense for other graph types;
31595
31794
  // ignore it if it somehow happens
@@ -31623,6 +31822,21 @@ function doMoveAll(state, action) {
31623
31822
  function doMovePoint(state, action) {
31624
31823
  switch (state.type) {
31625
31824
  case "polygon":
31825
+ const newCoords = setAtIndex({
31826
+ array: state.coords,
31827
+ index: action.index,
31828
+ newValue: boundAndSnapToGrid(action.destination, state)
31829
+ });
31830
+
31831
+ // Reject the move if it would cause the sides of the polygon to cross
31832
+ if (polygonSidesIntersect(newCoords)) {
31833
+ return state;
31834
+ }
31835
+ return {
31836
+ ...state,
31837
+ hasBeenInteractedWith: true,
31838
+ coords: newCoords
31839
+ };
31626
31840
  case "point":
31627
31841
  {
31628
31842
  return {
@@ -31631,11 +31845,27 @@ function doMovePoint(state, action) {
31631
31845
  coords: setAtIndex({
31632
31846
  array: state.coords,
31633
31847
  index: action.index,
31634
- newValue: snap(state.snapStep, bound$1({
31635
- snapStep: state.snapStep,
31636
- range: state.range,
31637
- point: action.destination
31638
- }))
31848
+ newValue: boundAndSnapToGrid(action.destination, state)
31849
+ })
31850
+ };
31851
+ }
31852
+ case "sinusoid":
31853
+ {
31854
+ // First, we need to verify that the new coordinates are not on the same vertical line
31855
+ // If they are, we don't want to move the point
31856
+ const destination = action.destination;
31857
+ const newCoords = [...state.coords];
31858
+ newCoords[action.index] = action.destination;
31859
+ if (newCoords[0][0] === newCoords[1][0]) {
31860
+ return state;
31861
+ }
31862
+ return {
31863
+ ...state,
31864
+ hasBeenInteractedWith: true,
31865
+ coords: setAtIndex({
31866
+ array: state.coords,
31867
+ index: action.index,
31868
+ newValue: boundAndSnapToGrid(destination, state)
31639
31869
  })
31640
31870
  };
31641
31871
  }
@@ -31647,11 +31877,7 @@ function doMovePoint(state, action) {
31647
31877
  coords: setAtIndex({
31648
31878
  array: state.coords,
31649
31879
  index: action.index,
31650
- newValue: snap(state.snapStep, bound$1({
31651
- snapStep: state.snapStep,
31652
- range: state.range,
31653
- point: action.destination
31654
- }))
31880
+ newValue: boundAndSnapToGrid(action.destination, state)
31655
31881
  })
31656
31882
  };
31657
31883
  }
@@ -31760,14 +31986,26 @@ const getChange = (coords, delta, constraintOpts) => {
31760
31986
  const dy = clamp(deltaY, minYMove, maxYMove);
31761
31987
  return [dx, dy];
31762
31988
  };
31989
+ function boundAndSnapToGrid(point, _ref) {
31990
+ let {
31991
+ snapStep,
31992
+ range
31993
+ } = _ref;
31994
+ return snap(snapStep, bound$1({
31995
+ snapStep,
31996
+ range,
31997
+ point
31998
+ }));
31999
+ }
32000
+
31763
32001
  // Returns the closest point to the given `point` that is within the graph
31764
32002
  // bounds given in `state`.
31765
- function bound$1(_ref) {
32003
+ function bound$1(_ref2) {
31766
32004
  let {
31767
32005
  snapStep,
31768
32006
  range,
31769
32007
  point
31770
- } = _ref;
32008
+ } = _ref2;
31771
32009
  const [requestedX, requestedY] = point;
31772
32010
  const [snapX, snapY] = snapStep;
31773
32011
  const [[minX, maxX], [minY, maxY]] = range;
@@ -31775,12 +32013,12 @@ function bound$1(_ref) {
31775
32013
  }
31776
32014
 
31777
32015
  // Returns the vector from the given point to the top-right corner of the graph
31778
- function maxMove(_ref2) {
32016
+ function maxMove(_ref3) {
31779
32017
  let {
31780
32018
  snapStep,
31781
32019
  range,
31782
32020
  point
31783
- } = _ref2;
32021
+ } = _ref3;
31784
32022
  const topRight = bound$1({
31785
32023
  snapStep,
31786
32024
  range,
@@ -31791,12 +32029,12 @@ function maxMove(_ref2) {
31791
32029
 
31792
32030
  // Returns the vector from the given point to the bottom-left corner of the
31793
32031
  // graph
31794
- function minMove(_ref3) {
32032
+ function minMove(_ref4) {
31795
32033
  let {
31796
32034
  snapStep,
31797
32035
  range,
31798
32036
  point
31799
- } = _ref3;
32037
+ } = _ref4;
31800
32038
  const bottomLeft = bound$1({
31801
32039
  snapStep,
31802
32040
  range,
@@ -31852,8 +32090,12 @@ const renderGraph = props => {
31852
32090
  graphState: state,
31853
32091
  dispatch: dispatch
31854
32092
  });
31855
- case "linear":
31856
32093
  case "linear-system":
32094
+ return /*#__PURE__*/React__namespace.createElement(LinearSystemGraph, {
32095
+ graphState: state,
32096
+ dispatch: dispatch
32097
+ });
32098
+ case "linear":
31857
32099
  return /*#__PURE__*/React__namespace.createElement(LinearGraph, {
31858
32100
  graphState: state,
31859
32101
  dispatch: dispatch
@@ -31883,6 +32125,11 @@ const renderGraph = props => {
31883
32125
  graphState: state,
31884
32126
  dispatch: dispatch
31885
32127
  });
32128
+ case "sinusoid":
32129
+ return /*#__PURE__*/React__namespace.createElement(SinusoidGraph, {
32130
+ graphState: state,
32131
+ dispatch: dispatch
32132
+ });
31886
32133
  default:
31887
32134
  return new wonderStuffCore.UnreachableCaseError(type);
31888
32135
  }
@@ -44096,7 +44343,7 @@ const init = function (options) {
44096
44343
 
44097
44344
  // This file is processed by a Rollup plugin (replace) to inject the production
44098
44345
  const libName = "@khanacademy/perseus";
44099
- const libVersion = "23.0.0";
44346
+ const libVersion = "23.3.0";
44100
44347
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
44101
44348
 
44102
44349
  const apiVersion = {
@@ -47196,6 +47443,7 @@ exports.itemDataVersion = itemDataVersion;
47196
47443
  exports.itemToTree = itemToTree;
47197
47444
  exports.itemVersion = ItemVersion;
47198
47445
  exports.libVersion = libVersion;
47446
+ exports.lockedEllipseFillStyles = lockedEllipseFillStyles;
47199
47447
  exports.lockedFigureColors = lockedFigureColors;
47200
47448
  exports.mediaQueries = mediaQueries;
47201
47449
  exports.parsePerseusItem = parsePerseusItem;