@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/es/index.js +341 -105
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +356 -108
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +2 -1
- package/dist/perseus-types.d.ts +20 -4
- package/dist/server-item-renderer.d.ts +2 -1
- package/dist/types.d.ts +2 -2
- package/dist/util/geometry.d.ts +1 -0
- package/dist/widgets/expression.d.ts +12 -6
- package/dist/widgets/interactive-graphs/graphs/components/css-cursor.d.ts +1 -0
- package/dist/widgets/interactive-graphs/graphs/components/movable-point-view.d.ts +2 -0
- package/dist/widgets/interactive-graphs/graphs/components/movable-point.d.ts +2 -0
- package/dist/widgets/interactive-graphs/graphs/index.d.ts +2 -0
- package/dist/widgets/interactive-graphs/graphs/linear-system.d.ts +5 -0
- package/dist/widgets/interactive-graphs/graphs/linear.d.ts +1 -1
- package/dist/widgets/interactive-graphs/graphs/sinusoid.d.ts +14 -0
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +8 -1
- package/dist/widgets/interactive-graphs/locked-ellipse.d.ts +4 -0
- package/dist/widgets/interactive-graphs/locked-vector.d.ts +4 -0
- package/dist/widgets/interactive-graphs/mafs-supported-graph-types.d.ts +1 -1
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-state.d.ts +1 -1
- package/dist/widgets/interactive-graphs/types.d.ts +12 -9
- package/dist/widgets/label-image.d.ts +4 -2
- package/package.json +1 -1
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
|
-
|
|
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,
|
|
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,
|
|
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:
|
|
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
|
-
|
|
29678
|
-
//
|
|
29679
|
-
, {
|
|
29680
|
-
points:
|
|
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:
|
|
30055
|
+
coords: getSegmentCoords({
|
|
29992
30056
|
graph,
|
|
29993
30057
|
step,
|
|
29994
30058
|
range
|
|
29995
30059
|
})
|
|
29996
30060
|
};
|
|
29997
30061
|
case "linear":
|
|
29998
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
30191
|
-
|
|
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
|
|
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
|
|
30231
|
-
|
|
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:
|
|
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:
|
|
31635
|
-
|
|
31636
|
-
|
|
31637
|
-
|
|
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:
|
|
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(
|
|
32003
|
+
function bound$1(_ref2) {
|
|
31766
32004
|
let {
|
|
31767
32005
|
snapStep,
|
|
31768
32006
|
range,
|
|
31769
32007
|
point
|
|
31770
|
-
} =
|
|
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(
|
|
32016
|
+
function maxMove(_ref3) {
|
|
31779
32017
|
let {
|
|
31780
32018
|
snapStep,
|
|
31781
32019
|
range,
|
|
31782
32020
|
point
|
|
31783
|
-
} =
|
|
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(
|
|
32032
|
+
function minMove(_ref4) {
|
|
31795
32033
|
let {
|
|
31796
32034
|
snapStep,
|
|
31797
32035
|
range,
|
|
31798
32036
|
point
|
|
31799
|
-
} =
|
|
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.
|
|
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;
|