@khanacademy/perseus 22.2.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.css +3 -6
- package/dist/es/index.js +447 -285
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +3 -6
- package/dist/index.js +467 -297
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +1 -0
- package/dist/perseus-types.d.ts +1 -2
- 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 +2 -2
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';
|
|
@@ -28664,15 +28664,10 @@ const ItemExtras = [
|
|
|
28664
28664
|
|
|
28665
28665
|
const PerseusExpressionAnswerFormConsidered = ["correct", "wrong", "ungraded"];
|
|
28666
28666
|
const lockedFigureColors = {
|
|
28667
|
-
blue: "#3D7586",
|
|
28668
28667
|
green: "#447A53",
|
|
28669
|
-
gray: "#5D5F66",
|
|
28670
28668
|
grayH: "#3B3D45",
|
|
28671
|
-
grayI: "#21242C",
|
|
28672
28669
|
purple: "#594094",
|
|
28673
|
-
purpleD: "#8351E8",
|
|
28674
28670
|
pink: "#B25071",
|
|
28675
|
-
orange: "#946700",
|
|
28676
28671
|
red: "#D92916"
|
|
28677
28672
|
};
|
|
28678
28673
|
const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
|
|
@@ -28731,6 +28726,7 @@ function Arrowhead(props) {
|
|
|
28731
28726
|
var _props$color;
|
|
28732
28727
|
const [point] = useTransformVectorsToPixels(props.tip);
|
|
28733
28728
|
return /*#__PURE__*/React.createElement("g", {
|
|
28729
|
+
className: "interactive-graph-arrowhead",
|
|
28734
28730
|
transform: `translate(${point[0]} ${point[1]}) rotate(${-props.angle})`
|
|
28735
28731
|
}, /*#__PURE__*/React.createElement("g", {
|
|
28736
28732
|
transform: "translate(-1.5)"
|
|
@@ -28745,49 +28741,7 @@ function Arrowhead(props) {
|
|
|
28745
28741
|
})));
|
|
28746
28742
|
}
|
|
28747
28743
|
|
|
28748
|
-
function
|
|
28749
|
-
const {
|
|
28750
|
-
start,
|
|
28751
|
-
end,
|
|
28752
|
-
style,
|
|
28753
|
-
className,
|
|
28754
|
-
testId
|
|
28755
|
-
} = props;
|
|
28756
|
-
return /*#__PURE__*/React.createElement("line", {
|
|
28757
|
-
x1: start[0],
|
|
28758
|
-
y1: start[1],
|
|
28759
|
-
x2: end[0],
|
|
28760
|
-
y2: end[1],
|
|
28761
|
-
style: style,
|
|
28762
|
-
className: className,
|
|
28763
|
-
"data-testid": testId
|
|
28764
|
-
});
|
|
28765
|
-
}
|
|
28766
|
-
|
|
28767
|
-
function Vector(props) {
|
|
28768
|
-
const {
|
|
28769
|
-
tail,
|
|
28770
|
-
tip,
|
|
28771
|
-
color,
|
|
28772
|
-
style
|
|
28773
|
-
} = props;
|
|
28774
|
-
const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
|
|
28775
|
-
const direction = vec.sub(tip, tail);
|
|
28776
|
-
return /*#__PURE__*/React.createElement("g", {
|
|
28777
|
-
style: {
|
|
28778
|
-
stroke: color,
|
|
28779
|
-
strokeWidth: 2
|
|
28780
|
-
}
|
|
28781
|
-
}, /*#__PURE__*/React.createElement(SVGLine, {
|
|
28782
|
-
start: tailPx,
|
|
28783
|
-
end: tipPx,
|
|
28784
|
-
style: style
|
|
28785
|
-
}), /*#__PURE__*/React.createElement(Arrowhead, {
|
|
28786
|
-
tip: tip,
|
|
28787
|
-
angle: angleDegrees(direction)
|
|
28788
|
-
}));
|
|
28789
|
-
}
|
|
28790
|
-
function angleDegrees([x, y]) {
|
|
28744
|
+
function calculateAngleInDegrees([x, y]) {
|
|
28791
28745
|
return Math.atan2(y, x) * 180 / Math.PI;
|
|
28792
28746
|
}
|
|
28793
28747
|
|
|
@@ -28829,10 +28783,57 @@ const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
|
|
|
28829
28783
|
return [xMax, yAtXMax];
|
|
28830
28784
|
}
|
|
28831
28785
|
};
|
|
28832
|
-
const getLines = points =>
|
|
28833
|
-
|
|
28834
|
-
|
|
28835
|
-
|
|
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
|
+
}
|
|
28836
28837
|
|
|
28837
28838
|
const LockedLine = props => {
|
|
28838
28839
|
const {
|
|
@@ -28848,27 +28849,38 @@ const LockedLine = props => {
|
|
|
28848
28849
|
let line;
|
|
28849
28850
|
if (kind === "ray") {
|
|
28850
28851
|
// Rays extend to the end of the graph in one direction.
|
|
28851
|
-
const
|
|
28852
|
+
const extendedPoint = getIntersectionOfRayWithBox(point2.coord, point1.coord, range);
|
|
28852
28853
|
line = /*#__PURE__*/React.createElement(Vector, {
|
|
28853
28854
|
tail: point1.coord,
|
|
28854
|
-
tip:
|
|
28855
|
+
tip: extendedPoint,
|
|
28855
28856
|
color: lockedFigureColors[color$1],
|
|
28856
28857
|
style: {
|
|
28857
|
-
strokeDasharray: lineStyle === "dashed" ?
|
|
28858
|
-
// TODO(lems-1930): Uncomment this line when the
|
|
28859
|
-
// dashed style is updated in Mafs.
|
|
28860
|
-
// ? "var(--mafs-line-stroke-dash-style)"
|
|
28861
|
-
"4, 3" : undefined
|
|
28858
|
+
strokeDasharray: lineStyle === "dashed" ? "var(--mafs-line-stroke-dash-style)" : undefined
|
|
28862
28859
|
}
|
|
28863
28860
|
});
|
|
28864
28861
|
} else {
|
|
28865
28862
|
const LineType = kind === "segment" ? Line$3.Segment : Line$3.ThroughPoints;
|
|
28866
|
-
|
|
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, {
|
|
28867
28879
|
point1: point1.coord,
|
|
28868
28880
|
point2: point2.coord,
|
|
28869
28881
|
color: lockedFigureColors[color$1],
|
|
28870
28882
|
style: lineStyle
|
|
28871
|
-
});
|
|
28883
|
+
}), endArrowHead);
|
|
28872
28884
|
}
|
|
28873
28885
|
return /*#__PURE__*/React.createElement("g", {
|
|
28874
28886
|
className: kind === "ray" ? "locked-ray" : "locked-line"
|
|
@@ -28974,6 +28986,20 @@ function movePoint(index, destination) {
|
|
|
28974
28986
|
destination
|
|
28975
28987
|
};
|
|
28976
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
|
+
}
|
|
28977
29003
|
const CHANGE_SNAP_STEP = "change-snap-step";
|
|
28978
29004
|
function changeSnapStep(snapStep) {
|
|
28979
29005
|
return {
|
|
@@ -29579,6 +29605,272 @@ const PolygonGraph = props => {
|
|
|
29579
29605
|
})));
|
|
29580
29606
|
};
|
|
29581
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
|
+
|
|
29582
29874
|
function PointGraph(props) {
|
|
29583
29875
|
const {
|
|
29584
29876
|
dispatch
|
|
@@ -30380,6 +30672,10 @@ function interactiveGraphReducer(state, action) {
|
|
|
30380
30672
|
return doMoveAll(state, action);
|
|
30381
30673
|
case MOVE_POINT:
|
|
30382
30674
|
return doMovePoint(state, action);
|
|
30675
|
+
case MOVE_CENTER:
|
|
30676
|
+
return doMoveCenter(state, action);
|
|
30677
|
+
case MOVE_RADIUS_POINT:
|
|
30678
|
+
return doMoveRadiusPoint(state, action);
|
|
30383
30679
|
case CHANGE_SNAP_STEP:
|
|
30384
30680
|
return doChangeSnapStep(state, action);
|
|
30385
30681
|
case CHANGE_RANGE:
|
|
@@ -30515,6 +30811,65 @@ function doMovePoint(state, action) {
|
|
|
30515
30811
|
throw new Error("The movePoint action is only for point and polygon graphs");
|
|
30516
30812
|
}
|
|
30517
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
|
+
}
|
|
30518
30873
|
function doChangeSnapStep(state, action) {
|
|
30519
30874
|
if (
|
|
30520
30875
|
// Deep equality check since these are arrays
|
|
@@ -30626,222 +30981,6 @@ function setAtIndex(args) {
|
|
|
30626
30981
|
return copy;
|
|
30627
30982
|
}
|
|
30628
30983
|
|
|
30629
|
-
function initializeGraphState(params) {
|
|
30630
|
-
const {
|
|
30631
|
-
graph,
|
|
30632
|
-
step,
|
|
30633
|
-
snapStep,
|
|
30634
|
-
range,
|
|
30635
|
-
markings
|
|
30636
|
-
} = params;
|
|
30637
|
-
const shared = {
|
|
30638
|
-
hasBeenInteractedWith: false,
|
|
30639
|
-
range,
|
|
30640
|
-
snapStep,
|
|
30641
|
-
markings
|
|
30642
|
-
};
|
|
30643
|
-
switch (graph.type) {
|
|
30644
|
-
case "segment":
|
|
30645
|
-
return _extends({}, shared, {
|
|
30646
|
-
type: "segment",
|
|
30647
|
-
coords: getDefaultSegments({
|
|
30648
|
-
graph,
|
|
30649
|
-
step,
|
|
30650
|
-
range
|
|
30651
|
-
})
|
|
30652
|
-
});
|
|
30653
|
-
case "linear":
|
|
30654
|
-
case "linear-system":
|
|
30655
|
-
case "ray":
|
|
30656
|
-
return _extends({}, shared, {
|
|
30657
|
-
type: graph.type,
|
|
30658
|
-
// Linear and ray graphs have a single tuple of points, while a
|
|
30659
|
-
// linear system has two tuples of points.
|
|
30660
|
-
coords: getLineCoords({
|
|
30661
|
-
graph,
|
|
30662
|
-
range,
|
|
30663
|
-
step
|
|
30664
|
-
})
|
|
30665
|
-
});
|
|
30666
|
-
case "polygon":
|
|
30667
|
-
return _extends({}, shared, {
|
|
30668
|
-
type: "polygon",
|
|
30669
|
-
showAngles: Boolean(graph.showAngles),
|
|
30670
|
-
showSides: Boolean(graph.showSides),
|
|
30671
|
-
coords: getPolygonCoords({
|
|
30672
|
-
graph,
|
|
30673
|
-
range,
|
|
30674
|
-
step
|
|
30675
|
-
})
|
|
30676
|
-
});
|
|
30677
|
-
case "point":
|
|
30678
|
-
return _extends({}, shared, {
|
|
30679
|
-
type: graph.type,
|
|
30680
|
-
coords: getDefaultPoints({
|
|
30681
|
-
graph,
|
|
30682
|
-
step,
|
|
30683
|
-
range
|
|
30684
|
-
})
|
|
30685
|
-
});
|
|
30686
|
-
case "angle":
|
|
30687
|
-
case "circle":
|
|
30688
|
-
case "sinusoid":
|
|
30689
|
-
case "quadratic":
|
|
30690
|
-
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
30691
|
-
default:
|
|
30692
|
-
throw new UnreachableCaseError(graph);
|
|
30693
|
-
}
|
|
30694
|
-
}
|
|
30695
|
-
const getDefaultPoints = ({
|
|
30696
|
-
graph,
|
|
30697
|
-
range,
|
|
30698
|
-
step
|
|
30699
|
-
}) => {
|
|
30700
|
-
var _graph$coords;
|
|
30701
|
-
const numPoints = graph.numPoints || 1;
|
|
30702
|
-
let coords = (_graph$coords = graph.coords) == null ? void 0 : _graph$coords.slice();
|
|
30703
|
-
if (coords) {
|
|
30704
|
-
return coords;
|
|
30705
|
-
}
|
|
30706
|
-
switch (numPoints) {
|
|
30707
|
-
case 1:
|
|
30708
|
-
// Back in the day, one point's coords were in graph.coord
|
|
30709
|
-
coords = [graph.coord || [0, 0]];
|
|
30710
|
-
break;
|
|
30711
|
-
case 2:
|
|
30712
|
-
coords = [[-5, 0], [5, 0]];
|
|
30713
|
-
break;
|
|
30714
|
-
case 3:
|
|
30715
|
-
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
30716
|
-
break;
|
|
30717
|
-
case 4:
|
|
30718
|
-
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
30719
|
-
break;
|
|
30720
|
-
case 5:
|
|
30721
|
-
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
30722
|
-
break;
|
|
30723
|
-
case 6:
|
|
30724
|
-
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
30725
|
-
break;
|
|
30726
|
-
default:
|
|
30727
|
-
coords = [];
|
|
30728
|
-
break;
|
|
30729
|
-
}
|
|
30730
|
-
// Transform coords from their -10 to 10 space to 0 to 1
|
|
30731
|
-
// because of the old graph.coord, and also it's easier.
|
|
30732
|
-
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
30733
|
-
return normalizePoints(range, step, newCoords);
|
|
30734
|
-
};
|
|
30735
|
-
|
|
30736
|
-
// TS v4 doesn't narrow return types, while v5 does.
|
|
30737
|
-
// Instead of updating to v5, using generic type to relate input and output types.
|
|
30738
|
-
function getGradableGraph(state, initialGraph) {
|
|
30739
|
-
if (!state.hasBeenInteractedWith) {
|
|
30740
|
-
return _extends({}, initialGraph);
|
|
30741
|
-
}
|
|
30742
|
-
if (initialGraph.type === "linear-system" && state.type === "linear-system") {
|
|
30743
|
-
return _extends({}, initialGraph, {
|
|
30744
|
-
coords: state.coords
|
|
30745
|
-
});
|
|
30746
|
-
}
|
|
30747
|
-
if (state.type === "segment" && initialGraph.type === "segment") {
|
|
30748
|
-
return _extends({}, initialGraph, {
|
|
30749
|
-
coords: state.coords
|
|
30750
|
-
});
|
|
30751
|
-
}
|
|
30752
|
-
if (state.type === "linear" && initialGraph.type === "linear") {
|
|
30753
|
-
return _extends({}, initialGraph, {
|
|
30754
|
-
coords: state.coords[0]
|
|
30755
|
-
});
|
|
30756
|
-
}
|
|
30757
|
-
if (state.type === "ray" && initialGraph.type === "ray") {
|
|
30758
|
-
return _extends({}, initialGraph, {
|
|
30759
|
-
coords: state.coords[0]
|
|
30760
|
-
});
|
|
30761
|
-
}
|
|
30762
|
-
if (state.type === "polygon" && initialGraph.type === "polygon") {
|
|
30763
|
-
return _extends({}, initialGraph, {
|
|
30764
|
-
coords: state.coords
|
|
30765
|
-
});
|
|
30766
|
-
}
|
|
30767
|
-
if (state.type === "point" && initialGraph.type === "point") {
|
|
30768
|
-
return _extends({}, initialGraph, {
|
|
30769
|
-
coords: state.coords
|
|
30770
|
-
});
|
|
30771
|
-
}
|
|
30772
|
-
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
30773
|
-
}
|
|
30774
|
-
const getDefaultSegments = ({
|
|
30775
|
-
graph,
|
|
30776
|
-
range,
|
|
30777
|
-
step
|
|
30778
|
-
}) => {
|
|
30779
|
-
const ys = n => {
|
|
30780
|
-
switch (n) {
|
|
30781
|
-
case 2:
|
|
30782
|
-
return [5, -5];
|
|
30783
|
-
case 3:
|
|
30784
|
-
return [5, 0, -5];
|
|
30785
|
-
case 4:
|
|
30786
|
-
return [6, 2, -2, -6];
|
|
30787
|
-
case 5:
|
|
30788
|
-
return [6, 3, 0, -3, -6];
|
|
30789
|
-
case 6:
|
|
30790
|
-
return [5, 3, 1, -1, -3, -5];
|
|
30791
|
-
default:
|
|
30792
|
-
return [5];
|
|
30793
|
-
}
|
|
30794
|
-
};
|
|
30795
|
-
const defaultRange = [[-10, 10], [-10, 10]];
|
|
30796
|
-
return ys(graph.numSegments).map(y => {
|
|
30797
|
-
let endpoints = [[-5, y], [5, y]];
|
|
30798
|
-
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
30799
|
-
endpoints = normalizePoints(range, step, endpoints);
|
|
30800
|
-
return endpoints;
|
|
30801
|
-
});
|
|
30802
|
-
};
|
|
30803
|
-
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
30804
|
-
const getLineCoords = ({
|
|
30805
|
-
graph,
|
|
30806
|
-
range,
|
|
30807
|
-
step
|
|
30808
|
-
}) => {
|
|
30809
|
-
var _graph$coords2;
|
|
30810
|
-
return (
|
|
30811
|
-
// Return two lines for a linear system, one for a ray or linear
|
|
30812
|
-
((_graph$coords2 = graph.coords) != null ? _graph$coords2 : graph.type === "linear-system") ? defaultLinearCoords.map(collinear => normalizePoints(range, step, collinear)) : [normalizePoints(range, step, defaultLinearCoords[0])]
|
|
30813
|
-
);
|
|
30814
|
-
};
|
|
30815
|
-
const getPolygonCoords = ({
|
|
30816
|
-
graph,
|
|
30817
|
-
range,
|
|
30818
|
-
step
|
|
30819
|
-
}) => {
|
|
30820
|
-
var _graph$coords3;
|
|
30821
|
-
let coords = (_graph$coords3 = graph.coords) == null ? void 0 : _graph$coords3.slice();
|
|
30822
|
-
if (coords) {
|
|
30823
|
-
return coords;
|
|
30824
|
-
}
|
|
30825
|
-
const n = graph.numSides || 3;
|
|
30826
|
-
if (n === "unlimited") {
|
|
30827
|
-
coords = [];
|
|
30828
|
-
} else {
|
|
30829
|
-
const angle = 2 * Math.PI / n;
|
|
30830
|
-
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
30831
|
-
|
|
30832
|
-
// TODO(alex): Generalize this to more than just triangles so that
|
|
30833
|
-
// all polygons have whole number side lengths if snapping to sides
|
|
30834
|
-
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
30835
|
-
|
|
30836
|
-
// Generate coords of a regular polygon with n sides
|
|
30837
|
-
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
30838
|
-
}
|
|
30839
|
-
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
30840
|
-
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
30841
|
-
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
30842
|
-
return coords;
|
|
30843
|
-
};
|
|
30844
|
-
|
|
30845
30984
|
const renderGraph = props => {
|
|
30846
30985
|
const {
|
|
30847
30986
|
state,
|
|
@@ -30878,20 +31017,42 @@ const renderGraph = props => {
|
|
|
30878
31017
|
dispatch: dispatch
|
|
30879
31018
|
});
|
|
30880
31019
|
case "circle":
|
|
30881
|
-
|
|
31020
|
+
return /*#__PURE__*/React.createElement(CircleGraph, {
|
|
31021
|
+
graphState: state,
|
|
31022
|
+
dispatch: dispatch
|
|
31023
|
+
});
|
|
30882
31024
|
default:
|
|
30883
31025
|
return new UnreachableCaseError(type);
|
|
30884
31026
|
}
|
|
30885
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
|
+
}
|
|
30886
31043
|
const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
30887
31044
|
const [state, dispatch] = React.useReducer(interactiveGraphReducer, props, initializeGraphState);
|
|
30888
31045
|
useImperativeHandle(ref, () => ({
|
|
30889
31046
|
getUserInput: () => getGradableGraph(state, props.graph)
|
|
30890
31047
|
}));
|
|
30891
|
-
|
|
31048
|
+
function onChange(next) {
|
|
31049
|
+
props.onChange(mafsStateToInteractiveGraph(next));
|
|
31050
|
+
}
|
|
31051
|
+
return /*#__PURE__*/React.createElement(MafsGraph, _extends({}, props, {
|
|
30892
31052
|
state: state,
|
|
30893
|
-
dispatch: dispatch
|
|
30894
|
-
|
|
31053
|
+
dispatch: dispatch,
|
|
31054
|
+
onChange: onChange
|
|
31055
|
+
}));
|
|
30895
31056
|
});
|
|
30896
31057
|
const MafsGraph = props => {
|
|
30897
31058
|
const {
|
|
@@ -30933,6 +31094,7 @@ const MafsGraph = props => {
|
|
|
30933
31094
|
labels
|
|
30934
31095
|
}
|
|
30935
31096
|
}, /*#__PURE__*/React.createElement(View, {
|
|
31097
|
+
className: "mafs-graph",
|
|
30936
31098
|
style: {
|
|
30937
31099
|
width,
|
|
30938
31100
|
height,
|
|
@@ -43069,7 +43231,7 @@ const init = function init(options) {
|
|
|
43069
43231
|
|
|
43070
43232
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
43071
43233
|
const libName = "@khanacademy/perseus";
|
|
43072
|
-
const libVersion = "22.
|
|
43234
|
+
const libVersion = "22.4.0";
|
|
43073
43235
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
43074
43236
|
|
|
43075
43237
|
const apiVersion = {
|