@khanacademy/perseus 22.3.0 → 22.4.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 +446 -280
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +466 -292
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +1 -0
- package/dist/perseus-types.d.ts +0 -1
- package/dist/server-item-renderer.d.ts +1 -0
- package/dist/types.d.ts +1 -1
- package/dist/widgets/expression.d.ts +6 -0
- package/dist/widgets/interactive-graphs/graphs/circle.d.ts +5 -0
- package/dist/widgets/interactive-graphs/graphs/index.d.ts +1 -0
- package/dist/widgets/interactive-graphs/graphs/utils.d.ts +1 -0
- package/dist/widgets/interactive-graphs/mafs-supported-graph-types.d.ts +1 -1
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-action.d.ts +13 -1
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-state.d.ts +8 -1
- package/dist/widgets/interactive-graphs/types.d.ts +2 -2
- package/dist/widgets/label-image.d.ts +2 -0
- package/package.json +1 -1
package/dist/es/index.js
CHANGED
|
@@ -26,10 +26,10 @@ import Button from '@khanacademy/wonder-blocks-button';
|
|
|
26
26
|
import { Strut } from '@khanacademy/wonder-blocks-layout';
|
|
27
27
|
import Banner from '@khanacademy/wonder-blocks-banner';
|
|
28
28
|
import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-blocks-dropdown';
|
|
29
|
-
import caretDown from '@phosphor-icons/core/
|
|
30
|
-
import caretUp from '@phosphor-icons/core/
|
|
29
|
+
import caretDown from '@phosphor-icons/core/regular/caret-down.svg';
|
|
30
|
+
import caretUp from '@phosphor-icons/core/regular/caret-up.svg';
|
|
31
31
|
import debounce$2 from 'lodash.debounce';
|
|
32
|
-
import { vec, Line as Line$3, Point as Point$2, useMovable, Text as Text$1, Polygon, Coordinates, Mafs } from 'mafs';
|
|
32
|
+
import { vec, Line as Line$3, Point as Point$2, useMovable, Text as Text$1, Polygon, Circle as Circle$1, Coordinates, Mafs } from 'mafs';
|
|
33
33
|
import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
|
|
34
34
|
import { Body, LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
|
|
35
35
|
import questionIcon from '@phosphor-icons/core/regular/question.svg';
|
|
@@ -28726,6 +28726,7 @@ function Arrowhead(props) {
|
|
|
28726
28726
|
var _props$color;
|
|
28727
28727
|
const [point] = useTransformVectorsToPixels(props.tip);
|
|
28728
28728
|
return /*#__PURE__*/React.createElement("g", {
|
|
28729
|
+
className: "interactive-graph-arrowhead",
|
|
28729
28730
|
transform: `translate(${point[0]} ${point[1]}) rotate(${-props.angle})`
|
|
28730
28731
|
}, /*#__PURE__*/React.createElement("g", {
|
|
28731
28732
|
transform: "translate(-1.5)"
|
|
@@ -28740,49 +28741,7 @@ function Arrowhead(props) {
|
|
|
28740
28741
|
})));
|
|
28741
28742
|
}
|
|
28742
28743
|
|
|
28743
|
-
function
|
|
28744
|
-
const {
|
|
28745
|
-
start,
|
|
28746
|
-
end,
|
|
28747
|
-
style,
|
|
28748
|
-
className,
|
|
28749
|
-
testId
|
|
28750
|
-
} = props;
|
|
28751
|
-
return /*#__PURE__*/React.createElement("line", {
|
|
28752
|
-
x1: start[0],
|
|
28753
|
-
y1: start[1],
|
|
28754
|
-
x2: end[0],
|
|
28755
|
-
y2: end[1],
|
|
28756
|
-
style: style,
|
|
28757
|
-
className: className,
|
|
28758
|
-
"data-testid": testId
|
|
28759
|
-
});
|
|
28760
|
-
}
|
|
28761
|
-
|
|
28762
|
-
function Vector(props) {
|
|
28763
|
-
const {
|
|
28764
|
-
tail,
|
|
28765
|
-
tip,
|
|
28766
|
-
color,
|
|
28767
|
-
style
|
|
28768
|
-
} = props;
|
|
28769
|
-
const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
|
|
28770
|
-
const direction = vec.sub(tip, tail);
|
|
28771
|
-
return /*#__PURE__*/React.createElement("g", {
|
|
28772
|
-
style: {
|
|
28773
|
-
stroke: color,
|
|
28774
|
-
strokeWidth: 2
|
|
28775
|
-
}
|
|
28776
|
-
}, /*#__PURE__*/React.createElement(SVGLine, {
|
|
28777
|
-
start: tailPx,
|
|
28778
|
-
end: tipPx,
|
|
28779
|
-
style: style
|
|
28780
|
-
}), /*#__PURE__*/React.createElement(Arrowhead, {
|
|
28781
|
-
tip: tip,
|
|
28782
|
-
angle: angleDegrees(direction)
|
|
28783
|
-
}));
|
|
28784
|
-
}
|
|
28785
|
-
function angleDegrees([x, y]) {
|
|
28744
|
+
function calculateAngleInDegrees([x, y]) {
|
|
28786
28745
|
return Math.atan2(y, x) * 180 / Math.PI;
|
|
28787
28746
|
}
|
|
28788
28747
|
|
|
@@ -28824,10 +28783,57 @@ const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
|
|
|
28824
28783
|
return [xMax, yAtXMax];
|
|
28825
28784
|
}
|
|
28826
28785
|
};
|
|
28827
|
-
const getLines = points =>
|
|
28828
|
-
|
|
28829
|
-
|
|
28830
|
-
|
|
28786
|
+
const getLines = points => {
|
|
28787
|
+
return points.map((point, i) => {
|
|
28788
|
+
const next = points[(i + 1) % points.length];
|
|
28789
|
+
return [point, next];
|
|
28790
|
+
});
|
|
28791
|
+
};
|
|
28792
|
+
|
|
28793
|
+
function SVGLine(props) {
|
|
28794
|
+
const {
|
|
28795
|
+
start,
|
|
28796
|
+
end,
|
|
28797
|
+
style,
|
|
28798
|
+
className,
|
|
28799
|
+
testId
|
|
28800
|
+
} = props;
|
|
28801
|
+
return /*#__PURE__*/React.createElement("line", {
|
|
28802
|
+
x1: start[0],
|
|
28803
|
+
y1: start[1],
|
|
28804
|
+
x2: end[0],
|
|
28805
|
+
y2: end[1],
|
|
28806
|
+
style: style,
|
|
28807
|
+
className: className,
|
|
28808
|
+
"data-testid": testId
|
|
28809
|
+
});
|
|
28810
|
+
}
|
|
28811
|
+
|
|
28812
|
+
function Vector(props) {
|
|
28813
|
+
const {
|
|
28814
|
+
tail,
|
|
28815
|
+
tip,
|
|
28816
|
+
color,
|
|
28817
|
+
style
|
|
28818
|
+
} = props;
|
|
28819
|
+
const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
|
|
28820
|
+
const direction = vec.sub(tip, tail);
|
|
28821
|
+
const angle = calculateAngleInDegrees(direction);
|
|
28822
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
28823
|
+
style: {
|
|
28824
|
+
stroke: color,
|
|
28825
|
+
strokeWidth: 2
|
|
28826
|
+
}
|
|
28827
|
+
}, /*#__PURE__*/React.createElement(SVGLine, {
|
|
28828
|
+
start: tailPx,
|
|
28829
|
+
end: tipPx,
|
|
28830
|
+
style: style
|
|
28831
|
+
}), /*#__PURE__*/React.createElement(Arrowhead, {
|
|
28832
|
+
angle: angle,
|
|
28833
|
+
tip: tip,
|
|
28834
|
+
color: color
|
|
28835
|
+
}));
|
|
28836
|
+
}
|
|
28831
28837
|
|
|
28832
28838
|
const LockedLine = props => {
|
|
28833
28839
|
const {
|
|
@@ -28843,27 +28849,38 @@ const LockedLine = props => {
|
|
|
28843
28849
|
let line;
|
|
28844
28850
|
if (kind === "ray") {
|
|
28845
28851
|
// Rays extend to the end of the graph in one direction.
|
|
28846
|
-
const
|
|
28852
|
+
const extendedPoint = getIntersectionOfRayWithBox(point2.coord, point1.coord, range);
|
|
28847
28853
|
line = /*#__PURE__*/React.createElement(Vector, {
|
|
28848
28854
|
tail: point1.coord,
|
|
28849
|
-
tip:
|
|
28855
|
+
tip: extendedPoint,
|
|
28850
28856
|
color: lockedFigureColors[color$1],
|
|
28851
28857
|
style: {
|
|
28852
|
-
strokeDasharray: lineStyle === "dashed" ?
|
|
28853
|
-
// TODO(lems-1930): Uncomment this line when the
|
|
28854
|
-
// dashed style is updated in Mafs.
|
|
28855
|
-
// ? "var(--mafs-line-stroke-dash-style)"
|
|
28856
|
-
"4, 3" : undefined
|
|
28858
|
+
strokeDasharray: lineStyle === "dashed" ? "var(--mafs-line-stroke-dash-style)" : undefined
|
|
28857
28859
|
}
|
|
28858
28860
|
});
|
|
28859
28861
|
} else {
|
|
28860
28862
|
const LineType = kind === "segment" ? Line$3.Segment : Line$3.ThroughPoints;
|
|
28861
|
-
|
|
28863
|
+
let arrowTip = kind === "segment" ? point2.coord : getIntersectionOfRayWithBox(point2.coord, point1.coord, range);
|
|
28864
|
+
const direction = vec.sub(point2.coord, point1.coord);
|
|
28865
|
+
let angle = calculateAngleInDegrees(direction);
|
|
28866
|
+
const startArrowHead = kind !== "segment" && /*#__PURE__*/React.createElement(Arrowhead, {
|
|
28867
|
+
angle: angle,
|
|
28868
|
+
tip: arrowTip,
|
|
28869
|
+
color: lockedFigureColors[color$1]
|
|
28870
|
+
});
|
|
28871
|
+
arrowTip = kind === "segment" ? point1.coord : getIntersectionOfRayWithBox(point1.coord, point2.coord, range);
|
|
28872
|
+
angle = angle > 180 ? angle - 180 : angle + 180;
|
|
28873
|
+
const endArrowHead = kind !== "segment" && /*#__PURE__*/React.createElement(Arrowhead, {
|
|
28874
|
+
angle: angle,
|
|
28875
|
+
tip: arrowTip,
|
|
28876
|
+
color: lockedFigureColors[color$1]
|
|
28877
|
+
});
|
|
28878
|
+
line = /*#__PURE__*/React.createElement(React.Fragment, null, startArrowHead, /*#__PURE__*/React.createElement(LineType, {
|
|
28862
28879
|
point1: point1.coord,
|
|
28863
28880
|
point2: point2.coord,
|
|
28864
28881
|
color: lockedFigureColors[color$1],
|
|
28865
28882
|
style: lineStyle
|
|
28866
|
-
});
|
|
28883
|
+
}), endArrowHead);
|
|
28867
28884
|
}
|
|
28868
28885
|
return /*#__PURE__*/React.createElement("g", {
|
|
28869
28886
|
className: kind === "ray" ? "locked-ray" : "locked-line"
|
|
@@ -28969,6 +28986,20 @@ function movePoint(index, destination) {
|
|
|
28969
28986
|
destination
|
|
28970
28987
|
};
|
|
28971
28988
|
}
|
|
28989
|
+
const MOVE_CENTER = "move-center";
|
|
28990
|
+
function moveCenter(destination) {
|
|
28991
|
+
return {
|
|
28992
|
+
type: MOVE_CENTER,
|
|
28993
|
+
destination
|
|
28994
|
+
};
|
|
28995
|
+
}
|
|
28996
|
+
const MOVE_RADIUS_POINT = "MOVE_RADIUS_POINT";
|
|
28997
|
+
function moveRadiusPoint(destination) {
|
|
28998
|
+
return {
|
|
28999
|
+
type: MOVE_RADIUS_POINT,
|
|
29000
|
+
destination
|
|
29001
|
+
};
|
|
29002
|
+
}
|
|
28972
29003
|
const CHANGE_SNAP_STEP = "change-snap-step";
|
|
28973
29004
|
function changeSnapStep(snapStep) {
|
|
28974
29005
|
return {
|
|
@@ -29574,6 +29605,272 @@ const PolygonGraph = props => {
|
|
|
29574
29605
|
})));
|
|
29575
29606
|
};
|
|
29576
29607
|
|
|
29608
|
+
function initializeGraphState(params) {
|
|
29609
|
+
const {
|
|
29610
|
+
graph,
|
|
29611
|
+
step,
|
|
29612
|
+
snapStep,
|
|
29613
|
+
range,
|
|
29614
|
+
markings
|
|
29615
|
+
} = params;
|
|
29616
|
+
const shared = {
|
|
29617
|
+
hasBeenInteractedWith: false,
|
|
29618
|
+
range,
|
|
29619
|
+
snapStep,
|
|
29620
|
+
markings
|
|
29621
|
+
};
|
|
29622
|
+
switch (graph.type) {
|
|
29623
|
+
case "segment":
|
|
29624
|
+
return _extends({}, shared, {
|
|
29625
|
+
type: "segment",
|
|
29626
|
+
coords: getDefaultSegments({
|
|
29627
|
+
graph,
|
|
29628
|
+
step,
|
|
29629
|
+
range
|
|
29630
|
+
})
|
|
29631
|
+
});
|
|
29632
|
+
case "linear":
|
|
29633
|
+
case "linear-system":
|
|
29634
|
+
case "ray":
|
|
29635
|
+
return _extends({}, shared, {
|
|
29636
|
+
type: graph.type,
|
|
29637
|
+
// Linear and ray graphs have a single tuple of points, while a
|
|
29638
|
+
// linear system has two tuples of points.
|
|
29639
|
+
coords: getLineCoords({
|
|
29640
|
+
graph,
|
|
29641
|
+
range,
|
|
29642
|
+
step
|
|
29643
|
+
})
|
|
29644
|
+
});
|
|
29645
|
+
case "polygon":
|
|
29646
|
+
return _extends({}, shared, {
|
|
29647
|
+
type: "polygon",
|
|
29648
|
+
showAngles: Boolean(graph.showAngles),
|
|
29649
|
+
showSides: Boolean(graph.showSides),
|
|
29650
|
+
coords: getPolygonCoords({
|
|
29651
|
+
graph,
|
|
29652
|
+
range,
|
|
29653
|
+
step
|
|
29654
|
+
})
|
|
29655
|
+
});
|
|
29656
|
+
case "point":
|
|
29657
|
+
return _extends({}, shared, {
|
|
29658
|
+
type: graph.type,
|
|
29659
|
+
coords: getDefaultPoints({
|
|
29660
|
+
graph,
|
|
29661
|
+
step,
|
|
29662
|
+
range
|
|
29663
|
+
})
|
|
29664
|
+
});
|
|
29665
|
+
case "circle":
|
|
29666
|
+
return _extends({}, shared, {
|
|
29667
|
+
type: graph.type,
|
|
29668
|
+
center: [0, 0],
|
|
29669
|
+
radiusPoint: [1, 0]
|
|
29670
|
+
});
|
|
29671
|
+
case "angle":
|
|
29672
|
+
case "sinusoid":
|
|
29673
|
+
case "quadratic":
|
|
29674
|
+
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
29675
|
+
default:
|
|
29676
|
+
throw new UnreachableCaseError(graph);
|
|
29677
|
+
}
|
|
29678
|
+
}
|
|
29679
|
+
const getDefaultPoints = ({
|
|
29680
|
+
graph,
|
|
29681
|
+
range,
|
|
29682
|
+
step
|
|
29683
|
+
}) => {
|
|
29684
|
+
var _graph$coords;
|
|
29685
|
+
const numPoints = graph.numPoints || 1;
|
|
29686
|
+
let coords = (_graph$coords = graph.coords) == null ? void 0 : _graph$coords.slice();
|
|
29687
|
+
if (coords) {
|
|
29688
|
+
return coords;
|
|
29689
|
+
}
|
|
29690
|
+
switch (numPoints) {
|
|
29691
|
+
case 1:
|
|
29692
|
+
// Back in the day, one point's coords were in graph.coord
|
|
29693
|
+
coords = [graph.coord || [0, 0]];
|
|
29694
|
+
break;
|
|
29695
|
+
case 2:
|
|
29696
|
+
coords = [[-5, 0], [5, 0]];
|
|
29697
|
+
break;
|
|
29698
|
+
case 3:
|
|
29699
|
+
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
29700
|
+
break;
|
|
29701
|
+
case 4:
|
|
29702
|
+
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
29703
|
+
break;
|
|
29704
|
+
case 5:
|
|
29705
|
+
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
29706
|
+
break;
|
|
29707
|
+
case 6:
|
|
29708
|
+
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
29709
|
+
break;
|
|
29710
|
+
default:
|
|
29711
|
+
coords = [];
|
|
29712
|
+
break;
|
|
29713
|
+
}
|
|
29714
|
+
// Transform coords from their -10 to 10 space to 0 to 1
|
|
29715
|
+
// because of the old graph.coord, and also it's easier.
|
|
29716
|
+
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
29717
|
+
return normalizePoints(range, step, newCoords);
|
|
29718
|
+
};
|
|
29719
|
+
|
|
29720
|
+
// TS v4 doesn't narrow return types, while v5 does.
|
|
29721
|
+
// Instead of updating to v5, using generic type to relate input and output types.
|
|
29722
|
+
function getGradableGraph(state, initialGraph) {
|
|
29723
|
+
if (!state.hasBeenInteractedWith) {
|
|
29724
|
+
return _extends({}, initialGraph);
|
|
29725
|
+
}
|
|
29726
|
+
if (initialGraph.type === "linear-system" && state.type === "linear-system") {
|
|
29727
|
+
return _extends({}, initialGraph, {
|
|
29728
|
+
coords: state.coords
|
|
29729
|
+
});
|
|
29730
|
+
}
|
|
29731
|
+
if (state.type === "segment" && initialGraph.type === "segment") {
|
|
29732
|
+
return _extends({}, initialGraph, {
|
|
29733
|
+
coords: state.coords
|
|
29734
|
+
});
|
|
29735
|
+
}
|
|
29736
|
+
if (state.type === "linear" && initialGraph.type === "linear") {
|
|
29737
|
+
return _extends({}, initialGraph, {
|
|
29738
|
+
coords: state.coords[0]
|
|
29739
|
+
});
|
|
29740
|
+
}
|
|
29741
|
+
if (state.type === "ray" && initialGraph.type === "ray") {
|
|
29742
|
+
return _extends({}, initialGraph, {
|
|
29743
|
+
coords: state.coords[0]
|
|
29744
|
+
});
|
|
29745
|
+
}
|
|
29746
|
+
if (state.type === "polygon" && initialGraph.type === "polygon") {
|
|
29747
|
+
return _extends({}, initialGraph, {
|
|
29748
|
+
coords: state.coords
|
|
29749
|
+
});
|
|
29750
|
+
}
|
|
29751
|
+
if (state.type === "point" && initialGraph.type === "point") {
|
|
29752
|
+
return _extends({}, initialGraph, {
|
|
29753
|
+
coords: state.coords
|
|
29754
|
+
});
|
|
29755
|
+
}
|
|
29756
|
+
if (state.type === "circle" && initialGraph.type === "circle") {
|
|
29757
|
+
return _extends({}, initialGraph, {
|
|
29758
|
+
center: state.center,
|
|
29759
|
+
radius: getRadius(state)
|
|
29760
|
+
});
|
|
29761
|
+
}
|
|
29762
|
+
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
29763
|
+
}
|
|
29764
|
+
const getDefaultSegments = ({
|
|
29765
|
+
graph,
|
|
29766
|
+
range,
|
|
29767
|
+
step
|
|
29768
|
+
}) => {
|
|
29769
|
+
const ys = n => {
|
|
29770
|
+
switch (n) {
|
|
29771
|
+
case 2:
|
|
29772
|
+
return [5, -5];
|
|
29773
|
+
case 3:
|
|
29774
|
+
return [5, 0, -5];
|
|
29775
|
+
case 4:
|
|
29776
|
+
return [6, 2, -2, -6];
|
|
29777
|
+
case 5:
|
|
29778
|
+
return [6, 3, 0, -3, -6];
|
|
29779
|
+
case 6:
|
|
29780
|
+
return [5, 3, 1, -1, -3, -5];
|
|
29781
|
+
default:
|
|
29782
|
+
return [5];
|
|
29783
|
+
}
|
|
29784
|
+
};
|
|
29785
|
+
const defaultRange = [[-10, 10], [-10, 10]];
|
|
29786
|
+
return ys(graph.numSegments).map(y => {
|
|
29787
|
+
let endpoints = [[-5, y], [5, y]];
|
|
29788
|
+
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
29789
|
+
endpoints = normalizePoints(range, step, endpoints);
|
|
29790
|
+
return endpoints;
|
|
29791
|
+
});
|
|
29792
|
+
};
|
|
29793
|
+
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
29794
|
+
const getLineCoords = ({
|
|
29795
|
+
graph,
|
|
29796
|
+
range,
|
|
29797
|
+
step
|
|
29798
|
+
}) => {
|
|
29799
|
+
var _graph$coords2;
|
|
29800
|
+
return (
|
|
29801
|
+
// Return two lines for a linear system, one for a ray or linear
|
|
29802
|
+
((_graph$coords2 = graph.coords) != null ? _graph$coords2 : graph.type === "linear-system") ? defaultLinearCoords.map(collinear => normalizePoints(range, step, collinear)) : [normalizePoints(range, step, defaultLinearCoords[0])]
|
|
29803
|
+
);
|
|
29804
|
+
};
|
|
29805
|
+
const getPolygonCoords = ({
|
|
29806
|
+
graph,
|
|
29807
|
+
range,
|
|
29808
|
+
step
|
|
29809
|
+
}) => {
|
|
29810
|
+
var _graph$coords3;
|
|
29811
|
+
let coords = (_graph$coords3 = graph.coords) == null ? void 0 : _graph$coords3.slice();
|
|
29812
|
+
if (coords) {
|
|
29813
|
+
return coords;
|
|
29814
|
+
}
|
|
29815
|
+
const n = graph.numSides || 3;
|
|
29816
|
+
if (n === "unlimited") {
|
|
29817
|
+
coords = [];
|
|
29818
|
+
} else {
|
|
29819
|
+
const angle = 2 * Math.PI / n;
|
|
29820
|
+
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
29821
|
+
|
|
29822
|
+
// TODO(alex): Generalize this to more than just triangles so that
|
|
29823
|
+
// all polygons have whole number side lengths if snapping to sides
|
|
29824
|
+
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
29825
|
+
|
|
29826
|
+
// Generate coords of a regular polygon with n sides
|
|
29827
|
+
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
29828
|
+
}
|
|
29829
|
+
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
29830
|
+
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
29831
|
+
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
29832
|
+
return coords;
|
|
29833
|
+
};
|
|
29834
|
+
|
|
29835
|
+
/**
|
|
29836
|
+
* determine radius of a circle graph
|
|
29837
|
+
*
|
|
29838
|
+
* @param graph - the graph object containing the circle
|
|
29839
|
+
* @returns the radius of the circle
|
|
29840
|
+
*/
|
|
29841
|
+
function getRadius(graph) {
|
|
29842
|
+
const [centerX, centerY] = graph.center;
|
|
29843
|
+
const [edgeX, edgeY] = graph.radiusPoint;
|
|
29844
|
+
return Math.sqrt(Math.pow(edgeX - centerX, 2) + Math.pow(edgeY - centerY, 2));
|
|
29845
|
+
}
|
|
29846
|
+
|
|
29847
|
+
function CircleGraph(props) {
|
|
29848
|
+
const {
|
|
29849
|
+
dispatch,
|
|
29850
|
+
graphState
|
|
29851
|
+
} = props;
|
|
29852
|
+
const {
|
|
29853
|
+
center,
|
|
29854
|
+
radiusPoint
|
|
29855
|
+
} = graphState;
|
|
29856
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Circle$1, {
|
|
29857
|
+
center: center,
|
|
29858
|
+
radius: getRadius(graphState),
|
|
29859
|
+
fillOpacity: 0,
|
|
29860
|
+
color: color.blue
|
|
29861
|
+
}), /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
29862
|
+
point: center,
|
|
29863
|
+
onMove: newCenter => {
|
|
29864
|
+
dispatch(moveCenter(newCenter));
|
|
29865
|
+
}
|
|
29866
|
+
}), /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
29867
|
+
point: radiusPoint,
|
|
29868
|
+
onMove: newRadiusPoint => {
|
|
29869
|
+
dispatch(moveRadiusPoint(newRadiusPoint));
|
|
29870
|
+
}
|
|
29871
|
+
}));
|
|
29872
|
+
}
|
|
29873
|
+
|
|
29577
29874
|
function PointGraph(props) {
|
|
29578
29875
|
const {
|
|
29579
29876
|
dispatch
|
|
@@ -30375,6 +30672,10 @@ function interactiveGraphReducer(state, action) {
|
|
|
30375
30672
|
return doMoveAll(state, action);
|
|
30376
30673
|
case MOVE_POINT:
|
|
30377
30674
|
return doMovePoint(state, action);
|
|
30675
|
+
case MOVE_CENTER:
|
|
30676
|
+
return doMoveCenter(state, action);
|
|
30677
|
+
case MOVE_RADIUS_POINT:
|
|
30678
|
+
return doMoveRadiusPoint(state, action);
|
|
30378
30679
|
case CHANGE_SNAP_STEP:
|
|
30379
30680
|
return doChangeSnapStep(state, action);
|
|
30380
30681
|
case CHANGE_RANGE:
|
|
@@ -30510,6 +30811,65 @@ function doMovePoint(state, action) {
|
|
|
30510
30811
|
throw new Error("The movePoint action is only for point and polygon graphs");
|
|
30511
30812
|
}
|
|
30512
30813
|
}
|
|
30814
|
+
function doMoveCenter(state, action) {
|
|
30815
|
+
switch (state.type) {
|
|
30816
|
+
case "circle":
|
|
30817
|
+
{
|
|
30818
|
+
// Constrain the center of the circle to the chart range
|
|
30819
|
+
const constrainedCenter = bound$1({
|
|
30820
|
+
snapStep: state.snapStep,
|
|
30821
|
+
range: state.range,
|
|
30822
|
+
point: action.destination
|
|
30823
|
+
});
|
|
30824
|
+
|
|
30825
|
+
// Reposition the radius point based on the new center
|
|
30826
|
+
// (spread to make sure we're not going to mutate anything)
|
|
30827
|
+
const newRadiusPoint = [...vec.add(state.radiusPoint, vec.sub(constrainedCenter, state.center))];
|
|
30828
|
+
|
|
30829
|
+
// Try to position the radius handle in a visible spot
|
|
30830
|
+
// if it otherwise would be off the chart
|
|
30831
|
+
// ex: if the handle is on the right and we move the center
|
|
30832
|
+
// to the rightmost position, move the handle to the left
|
|
30833
|
+
const [xMin, xMax] = state.range[0];
|
|
30834
|
+
const [radX] = newRadiusPoint;
|
|
30835
|
+
if (radX < xMin || radX > xMax) {
|
|
30836
|
+
const xJumpDist = (radX - constrainedCenter[0]) * 2;
|
|
30837
|
+
const possibleNewX = radX - xJumpDist;
|
|
30838
|
+
if (possibleNewX >= xMin && possibleNewX <= xMax) {
|
|
30839
|
+
newRadiusPoint[0] = possibleNewX;
|
|
30840
|
+
}
|
|
30841
|
+
}
|
|
30842
|
+
return _extends({}, state, {
|
|
30843
|
+
hasBeenInteractedWith: true,
|
|
30844
|
+
center: constrainedCenter,
|
|
30845
|
+
radiusPoint: newRadiusPoint
|
|
30846
|
+
});
|
|
30847
|
+
}
|
|
30848
|
+
default:
|
|
30849
|
+
throw new Error("The doMoveCenter action is only for circle graphs");
|
|
30850
|
+
}
|
|
30851
|
+
}
|
|
30852
|
+
function doMoveRadiusPoint(state, action) {
|
|
30853
|
+
switch (state.type) {
|
|
30854
|
+
case "circle":
|
|
30855
|
+
{
|
|
30856
|
+
const [xMin, xMax] = state.range[0];
|
|
30857
|
+
const nextRadiusPoint = [
|
|
30858
|
+
// Constrain to graph range
|
|
30859
|
+
// The +0 is to convert -0 to +0
|
|
30860
|
+
Math.min(Math.max(xMin, action.destination[0] + 0), xMax), state.center[1]];
|
|
30861
|
+
if (_.isEqual(nextRadiusPoint, state.center)) {
|
|
30862
|
+
return state;
|
|
30863
|
+
}
|
|
30864
|
+
return _extends({}, state, {
|
|
30865
|
+
hasBeenInteractedWith: true,
|
|
30866
|
+
radiusPoint: nextRadiusPoint
|
|
30867
|
+
});
|
|
30868
|
+
}
|
|
30869
|
+
default:
|
|
30870
|
+
throw new Error("The doMoveRadiusPoint action is only for circle graphs");
|
|
30871
|
+
}
|
|
30872
|
+
}
|
|
30513
30873
|
function doChangeSnapStep(state, action) {
|
|
30514
30874
|
if (
|
|
30515
30875
|
// Deep equality check since these are arrays
|
|
@@ -30621,222 +30981,6 @@ function setAtIndex(args) {
|
|
|
30621
30981
|
return copy;
|
|
30622
30982
|
}
|
|
30623
30983
|
|
|
30624
|
-
function initializeGraphState(params) {
|
|
30625
|
-
const {
|
|
30626
|
-
graph,
|
|
30627
|
-
step,
|
|
30628
|
-
snapStep,
|
|
30629
|
-
range,
|
|
30630
|
-
markings
|
|
30631
|
-
} = params;
|
|
30632
|
-
const shared = {
|
|
30633
|
-
hasBeenInteractedWith: false,
|
|
30634
|
-
range,
|
|
30635
|
-
snapStep,
|
|
30636
|
-
markings
|
|
30637
|
-
};
|
|
30638
|
-
switch (graph.type) {
|
|
30639
|
-
case "segment":
|
|
30640
|
-
return _extends({}, shared, {
|
|
30641
|
-
type: "segment",
|
|
30642
|
-
coords: getDefaultSegments({
|
|
30643
|
-
graph,
|
|
30644
|
-
step,
|
|
30645
|
-
range
|
|
30646
|
-
})
|
|
30647
|
-
});
|
|
30648
|
-
case "linear":
|
|
30649
|
-
case "linear-system":
|
|
30650
|
-
case "ray":
|
|
30651
|
-
return _extends({}, shared, {
|
|
30652
|
-
type: graph.type,
|
|
30653
|
-
// Linear and ray graphs have a single tuple of points, while a
|
|
30654
|
-
// linear system has two tuples of points.
|
|
30655
|
-
coords: getLineCoords({
|
|
30656
|
-
graph,
|
|
30657
|
-
range,
|
|
30658
|
-
step
|
|
30659
|
-
})
|
|
30660
|
-
});
|
|
30661
|
-
case "polygon":
|
|
30662
|
-
return _extends({}, shared, {
|
|
30663
|
-
type: "polygon",
|
|
30664
|
-
showAngles: Boolean(graph.showAngles),
|
|
30665
|
-
showSides: Boolean(graph.showSides),
|
|
30666
|
-
coords: getPolygonCoords({
|
|
30667
|
-
graph,
|
|
30668
|
-
range,
|
|
30669
|
-
step
|
|
30670
|
-
})
|
|
30671
|
-
});
|
|
30672
|
-
case "point":
|
|
30673
|
-
return _extends({}, shared, {
|
|
30674
|
-
type: graph.type,
|
|
30675
|
-
coords: getDefaultPoints({
|
|
30676
|
-
graph,
|
|
30677
|
-
step,
|
|
30678
|
-
range
|
|
30679
|
-
})
|
|
30680
|
-
});
|
|
30681
|
-
case "angle":
|
|
30682
|
-
case "circle":
|
|
30683
|
-
case "sinusoid":
|
|
30684
|
-
case "quadratic":
|
|
30685
|
-
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
30686
|
-
default:
|
|
30687
|
-
throw new UnreachableCaseError(graph);
|
|
30688
|
-
}
|
|
30689
|
-
}
|
|
30690
|
-
const getDefaultPoints = ({
|
|
30691
|
-
graph,
|
|
30692
|
-
range,
|
|
30693
|
-
step
|
|
30694
|
-
}) => {
|
|
30695
|
-
var _graph$coords;
|
|
30696
|
-
const numPoints = graph.numPoints || 1;
|
|
30697
|
-
let coords = (_graph$coords = graph.coords) == null ? void 0 : _graph$coords.slice();
|
|
30698
|
-
if (coords) {
|
|
30699
|
-
return coords;
|
|
30700
|
-
}
|
|
30701
|
-
switch (numPoints) {
|
|
30702
|
-
case 1:
|
|
30703
|
-
// Back in the day, one point's coords were in graph.coord
|
|
30704
|
-
coords = [graph.coord || [0, 0]];
|
|
30705
|
-
break;
|
|
30706
|
-
case 2:
|
|
30707
|
-
coords = [[-5, 0], [5, 0]];
|
|
30708
|
-
break;
|
|
30709
|
-
case 3:
|
|
30710
|
-
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
30711
|
-
break;
|
|
30712
|
-
case 4:
|
|
30713
|
-
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
30714
|
-
break;
|
|
30715
|
-
case 5:
|
|
30716
|
-
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
30717
|
-
break;
|
|
30718
|
-
case 6:
|
|
30719
|
-
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
30720
|
-
break;
|
|
30721
|
-
default:
|
|
30722
|
-
coords = [];
|
|
30723
|
-
break;
|
|
30724
|
-
}
|
|
30725
|
-
// Transform coords from their -10 to 10 space to 0 to 1
|
|
30726
|
-
// because of the old graph.coord, and also it's easier.
|
|
30727
|
-
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
30728
|
-
return normalizePoints(range, step, newCoords);
|
|
30729
|
-
};
|
|
30730
|
-
|
|
30731
|
-
// TS v4 doesn't narrow return types, while v5 does.
|
|
30732
|
-
// Instead of updating to v5, using generic type to relate input and output types.
|
|
30733
|
-
function getGradableGraph(state, initialGraph) {
|
|
30734
|
-
if (!state.hasBeenInteractedWith) {
|
|
30735
|
-
return _extends({}, initialGraph);
|
|
30736
|
-
}
|
|
30737
|
-
if (initialGraph.type === "linear-system" && state.type === "linear-system") {
|
|
30738
|
-
return _extends({}, initialGraph, {
|
|
30739
|
-
coords: state.coords
|
|
30740
|
-
});
|
|
30741
|
-
}
|
|
30742
|
-
if (state.type === "segment" && initialGraph.type === "segment") {
|
|
30743
|
-
return _extends({}, initialGraph, {
|
|
30744
|
-
coords: state.coords
|
|
30745
|
-
});
|
|
30746
|
-
}
|
|
30747
|
-
if (state.type === "linear" && initialGraph.type === "linear") {
|
|
30748
|
-
return _extends({}, initialGraph, {
|
|
30749
|
-
coords: state.coords[0]
|
|
30750
|
-
});
|
|
30751
|
-
}
|
|
30752
|
-
if (state.type === "ray" && initialGraph.type === "ray") {
|
|
30753
|
-
return _extends({}, initialGraph, {
|
|
30754
|
-
coords: state.coords[0]
|
|
30755
|
-
});
|
|
30756
|
-
}
|
|
30757
|
-
if (state.type === "polygon" && initialGraph.type === "polygon") {
|
|
30758
|
-
return _extends({}, initialGraph, {
|
|
30759
|
-
coords: state.coords
|
|
30760
|
-
});
|
|
30761
|
-
}
|
|
30762
|
-
if (state.type === "point" && initialGraph.type === "point") {
|
|
30763
|
-
return _extends({}, initialGraph, {
|
|
30764
|
-
coords: state.coords
|
|
30765
|
-
});
|
|
30766
|
-
}
|
|
30767
|
-
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
30768
|
-
}
|
|
30769
|
-
const getDefaultSegments = ({
|
|
30770
|
-
graph,
|
|
30771
|
-
range,
|
|
30772
|
-
step
|
|
30773
|
-
}) => {
|
|
30774
|
-
const ys = n => {
|
|
30775
|
-
switch (n) {
|
|
30776
|
-
case 2:
|
|
30777
|
-
return [5, -5];
|
|
30778
|
-
case 3:
|
|
30779
|
-
return [5, 0, -5];
|
|
30780
|
-
case 4:
|
|
30781
|
-
return [6, 2, -2, -6];
|
|
30782
|
-
case 5:
|
|
30783
|
-
return [6, 3, 0, -3, -6];
|
|
30784
|
-
case 6:
|
|
30785
|
-
return [5, 3, 1, -1, -3, -5];
|
|
30786
|
-
default:
|
|
30787
|
-
return [5];
|
|
30788
|
-
}
|
|
30789
|
-
};
|
|
30790
|
-
const defaultRange = [[-10, 10], [-10, 10]];
|
|
30791
|
-
return ys(graph.numSegments).map(y => {
|
|
30792
|
-
let endpoints = [[-5, y], [5, y]];
|
|
30793
|
-
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
30794
|
-
endpoints = normalizePoints(range, step, endpoints);
|
|
30795
|
-
return endpoints;
|
|
30796
|
-
});
|
|
30797
|
-
};
|
|
30798
|
-
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
30799
|
-
const getLineCoords = ({
|
|
30800
|
-
graph,
|
|
30801
|
-
range,
|
|
30802
|
-
step
|
|
30803
|
-
}) => {
|
|
30804
|
-
var _graph$coords2;
|
|
30805
|
-
return (
|
|
30806
|
-
// Return two lines for a linear system, one for a ray or linear
|
|
30807
|
-
((_graph$coords2 = graph.coords) != null ? _graph$coords2 : graph.type === "linear-system") ? defaultLinearCoords.map(collinear => normalizePoints(range, step, collinear)) : [normalizePoints(range, step, defaultLinearCoords[0])]
|
|
30808
|
-
);
|
|
30809
|
-
};
|
|
30810
|
-
const getPolygonCoords = ({
|
|
30811
|
-
graph,
|
|
30812
|
-
range,
|
|
30813
|
-
step
|
|
30814
|
-
}) => {
|
|
30815
|
-
var _graph$coords3;
|
|
30816
|
-
let coords = (_graph$coords3 = graph.coords) == null ? void 0 : _graph$coords3.slice();
|
|
30817
|
-
if (coords) {
|
|
30818
|
-
return coords;
|
|
30819
|
-
}
|
|
30820
|
-
const n = graph.numSides || 3;
|
|
30821
|
-
if (n === "unlimited") {
|
|
30822
|
-
coords = [];
|
|
30823
|
-
} else {
|
|
30824
|
-
const angle = 2 * Math.PI / n;
|
|
30825
|
-
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
30826
|
-
|
|
30827
|
-
// TODO(alex): Generalize this to more than just triangles so that
|
|
30828
|
-
// all polygons have whole number side lengths if snapping to sides
|
|
30829
|
-
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
30830
|
-
|
|
30831
|
-
// Generate coords of a regular polygon with n sides
|
|
30832
|
-
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
30833
|
-
}
|
|
30834
|
-
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
30835
|
-
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
30836
|
-
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
30837
|
-
return coords;
|
|
30838
|
-
};
|
|
30839
|
-
|
|
30840
30984
|
const renderGraph = props => {
|
|
30841
30985
|
const {
|
|
30842
30986
|
state,
|
|
@@ -30873,20 +31017,42 @@ const renderGraph = props => {
|
|
|
30873
31017
|
dispatch: dispatch
|
|
30874
31018
|
});
|
|
30875
31019
|
case "circle":
|
|
30876
|
-
|
|
31020
|
+
return /*#__PURE__*/React.createElement(CircleGraph, {
|
|
31021
|
+
graphState: state,
|
|
31022
|
+
dispatch: dispatch
|
|
31023
|
+
});
|
|
30877
31024
|
default:
|
|
30878
31025
|
return new UnreachableCaseError(type);
|
|
30879
31026
|
}
|
|
30880
31027
|
};
|
|
31028
|
+
|
|
31029
|
+
// Rather than be tightly bound to how data was structured in
|
|
31030
|
+
// the legacy interactive graph, this lets us store state
|
|
31031
|
+
// however we want and we just transform it before handing it off
|
|
31032
|
+
// the the parent InteractiveGraph
|
|
31033
|
+
function mafsStateToInteractiveGraph(state) {
|
|
31034
|
+
if (state.graph.type === "circle") {
|
|
31035
|
+
return _extends({}, state, {
|
|
31036
|
+
graph: _extends({}, state.graph, {
|
|
31037
|
+
radius: getRadius(state.graph)
|
|
31038
|
+
})
|
|
31039
|
+
});
|
|
31040
|
+
}
|
|
31041
|
+
return _extends({}, state);
|
|
31042
|
+
}
|
|
30881
31043
|
const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
30882
31044
|
const [state, dispatch] = React.useReducer(interactiveGraphReducer, props, initializeGraphState);
|
|
30883
31045
|
useImperativeHandle(ref, () => ({
|
|
30884
31046
|
getUserInput: () => getGradableGraph(state, props.graph)
|
|
30885
31047
|
}));
|
|
30886
|
-
|
|
31048
|
+
function onChange(next) {
|
|
31049
|
+
props.onChange(mafsStateToInteractiveGraph(next));
|
|
31050
|
+
}
|
|
31051
|
+
return /*#__PURE__*/React.createElement(MafsGraph, _extends({}, props, {
|
|
30887
31052
|
state: state,
|
|
30888
|
-
dispatch: dispatch
|
|
30889
|
-
|
|
31053
|
+
dispatch: dispatch,
|
|
31054
|
+
onChange: onChange
|
|
31055
|
+
}));
|
|
30890
31056
|
});
|
|
30891
31057
|
const MafsGraph = props => {
|
|
30892
31058
|
const {
|
|
@@ -43065,7 +43231,7 @@ const init = function init(options) {
|
|
|
43065
43231
|
|
|
43066
43232
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
43067
43233
|
const libName = "@khanacademy/perseus";
|
|
43068
|
-
const libVersion = "22.
|
|
43234
|
+
const libVersion = "22.4.0";
|
|
43069
43235
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
43070
43236
|
|
|
43071
43237
|
const apiVersion = {
|