@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/index.js
CHANGED
|
@@ -27,8 +27,8 @@ var Button = require('@khanacademy/wonder-blocks-button');
|
|
|
27
27
|
var wonderBlocksLayout = require('@khanacademy/wonder-blocks-layout');
|
|
28
28
|
var Banner = require('@khanacademy/wonder-blocks-banner');
|
|
29
29
|
var wonderBlocksDropdown = require('@khanacademy/wonder-blocks-dropdown');
|
|
30
|
-
var caretDown = require('@phosphor-icons/core/
|
|
31
|
-
var caretUp = require('@phosphor-icons/core/
|
|
30
|
+
var caretDown = require('@phosphor-icons/core/regular/caret-down.svg');
|
|
31
|
+
var caretUp = require('@phosphor-icons/core/regular/caret-up.svg');
|
|
32
32
|
var debounce$1 = require('lodash.debounce');
|
|
33
33
|
var mafs = require('mafs');
|
|
34
34
|
var wonderBlocksIcon = require('@khanacademy/wonder-blocks-icon');
|
|
@@ -28952,15 +28952,10 @@ const ItemExtras = [
|
|
|
28952
28952
|
|
|
28953
28953
|
const PerseusExpressionAnswerFormConsidered = ["correct", "wrong", "ungraded"];
|
|
28954
28954
|
const lockedFigureColors = {
|
|
28955
|
-
blue: "#3D7586",
|
|
28956
28955
|
green: "#447A53",
|
|
28957
|
-
gray: "#5D5F66",
|
|
28958
28956
|
grayH: "#3B3D45",
|
|
28959
|
-
grayI: "#21242C",
|
|
28960
28957
|
purple: "#594094",
|
|
28961
|
-
purpleD: "#8351E8",
|
|
28962
28958
|
pink: "#B25071",
|
|
28963
|
-
orange: "#946700",
|
|
28964
28959
|
red: "#D92916"
|
|
28965
28960
|
};
|
|
28966
28961
|
const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
|
|
@@ -29017,6 +29012,7 @@ const arrowPath = pathBuilder().move(-3, 4).curve(-2.75, 2.5, 0, 0.25, 0.75, 0).
|
|
|
29017
29012
|
function Arrowhead(props) {
|
|
29018
29013
|
const [point] = useTransformVectorsToPixels(props.tip);
|
|
29019
29014
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29015
|
+
className: "interactive-graph-arrowhead",
|
|
29020
29016
|
transform: `translate(${point[0]} ${point[1]}) rotate(${-props.angle})`
|
|
29021
29017
|
}, /*#__PURE__*/React__namespace.createElement("g", {
|
|
29022
29018
|
transform: "translate(-1.5)"
|
|
@@ -29031,49 +29027,7 @@ function Arrowhead(props) {
|
|
|
29031
29027
|
})));
|
|
29032
29028
|
}
|
|
29033
29029
|
|
|
29034
|
-
function
|
|
29035
|
-
const {
|
|
29036
|
-
start,
|
|
29037
|
-
end,
|
|
29038
|
-
style,
|
|
29039
|
-
className,
|
|
29040
|
-
testId
|
|
29041
|
-
} = props;
|
|
29042
|
-
return /*#__PURE__*/React__namespace.createElement("line", {
|
|
29043
|
-
x1: start[0],
|
|
29044
|
-
y1: start[1],
|
|
29045
|
-
x2: end[0],
|
|
29046
|
-
y2: end[1],
|
|
29047
|
-
style: style,
|
|
29048
|
-
className: className,
|
|
29049
|
-
"data-testid": testId
|
|
29050
|
-
});
|
|
29051
|
-
}
|
|
29052
|
-
|
|
29053
|
-
function Vector(props) {
|
|
29054
|
-
const {
|
|
29055
|
-
tail,
|
|
29056
|
-
tip,
|
|
29057
|
-
color,
|
|
29058
|
-
style
|
|
29059
|
-
} = props;
|
|
29060
|
-
const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
|
|
29061
|
-
const direction = mafs.vec.sub(tip, tail);
|
|
29062
|
-
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29063
|
-
style: {
|
|
29064
|
-
stroke: color,
|
|
29065
|
-
strokeWidth: 2
|
|
29066
|
-
}
|
|
29067
|
-
}, /*#__PURE__*/React__namespace.createElement(SVGLine, {
|
|
29068
|
-
start: tailPx,
|
|
29069
|
-
end: tipPx,
|
|
29070
|
-
style: style
|
|
29071
|
-
}), /*#__PURE__*/React__namespace.createElement(Arrowhead, {
|
|
29072
|
-
tip: tip,
|
|
29073
|
-
angle: angleDegrees(direction)
|
|
29074
|
-
}));
|
|
29075
|
-
}
|
|
29076
|
-
function angleDegrees(_ref) {
|
|
29030
|
+
function calculateAngleInDegrees(_ref) {
|
|
29077
29031
|
let [x, y] = _ref;
|
|
29078
29032
|
return Math.atan2(y, x) * 180 / Math.PI;
|
|
29079
29033
|
}
|
|
@@ -29116,10 +29070,57 @@ const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
|
|
|
29116
29070
|
return [xMax, yAtXMax];
|
|
29117
29071
|
}
|
|
29118
29072
|
};
|
|
29119
|
-
const getLines = points =>
|
|
29120
|
-
|
|
29121
|
-
|
|
29122
|
-
|
|
29073
|
+
const getLines = points => {
|
|
29074
|
+
return points.map((point, i) => {
|
|
29075
|
+
const next = points[(i + 1) % points.length];
|
|
29076
|
+
return [point, next];
|
|
29077
|
+
});
|
|
29078
|
+
};
|
|
29079
|
+
|
|
29080
|
+
function SVGLine(props) {
|
|
29081
|
+
const {
|
|
29082
|
+
start,
|
|
29083
|
+
end,
|
|
29084
|
+
style,
|
|
29085
|
+
className,
|
|
29086
|
+
testId
|
|
29087
|
+
} = props;
|
|
29088
|
+
return /*#__PURE__*/React__namespace.createElement("line", {
|
|
29089
|
+
x1: start[0],
|
|
29090
|
+
y1: start[1],
|
|
29091
|
+
x2: end[0],
|
|
29092
|
+
y2: end[1],
|
|
29093
|
+
style: style,
|
|
29094
|
+
className: className,
|
|
29095
|
+
"data-testid": testId
|
|
29096
|
+
});
|
|
29097
|
+
}
|
|
29098
|
+
|
|
29099
|
+
function Vector(props) {
|
|
29100
|
+
const {
|
|
29101
|
+
tail,
|
|
29102
|
+
tip,
|
|
29103
|
+
color,
|
|
29104
|
+
style
|
|
29105
|
+
} = props;
|
|
29106
|
+
const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
|
|
29107
|
+
const direction = mafs.vec.sub(tip, tail);
|
|
29108
|
+
const angle = calculateAngleInDegrees(direction);
|
|
29109
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29110
|
+
style: {
|
|
29111
|
+
stroke: color,
|
|
29112
|
+
strokeWidth: 2
|
|
29113
|
+
}
|
|
29114
|
+
}, /*#__PURE__*/React__namespace.createElement(SVGLine, {
|
|
29115
|
+
start: tailPx,
|
|
29116
|
+
end: tipPx,
|
|
29117
|
+
style: style
|
|
29118
|
+
}), /*#__PURE__*/React__namespace.createElement(Arrowhead, {
|
|
29119
|
+
angle: angle,
|
|
29120
|
+
tip: tip,
|
|
29121
|
+
color: color
|
|
29122
|
+
}));
|
|
29123
|
+
}
|
|
29123
29124
|
|
|
29124
29125
|
const LockedLine = props => {
|
|
29125
29126
|
const {
|
|
@@ -29135,27 +29136,38 @@ const LockedLine = props => {
|
|
|
29135
29136
|
let line;
|
|
29136
29137
|
if (kind === "ray") {
|
|
29137
29138
|
// Rays extend to the end of the graph in one direction.
|
|
29138
|
-
const
|
|
29139
|
+
const extendedPoint = getIntersectionOfRayWithBox(point2.coord, point1.coord, range);
|
|
29139
29140
|
line = /*#__PURE__*/React__namespace.createElement(Vector, {
|
|
29140
29141
|
tail: point1.coord,
|
|
29141
|
-
tip:
|
|
29142
|
+
tip: extendedPoint,
|
|
29142
29143
|
color: lockedFigureColors[color],
|
|
29143
29144
|
style: {
|
|
29144
|
-
strokeDasharray: lineStyle === "dashed" ?
|
|
29145
|
-
// TODO(lems-1930): Uncomment this line when the
|
|
29146
|
-
// dashed style is updated in Mafs.
|
|
29147
|
-
// ? "var(--mafs-line-stroke-dash-style)"
|
|
29148
|
-
"4, 3" : undefined
|
|
29145
|
+
strokeDasharray: lineStyle === "dashed" ? "var(--mafs-line-stroke-dash-style)" : undefined
|
|
29149
29146
|
}
|
|
29150
29147
|
});
|
|
29151
29148
|
} else {
|
|
29152
29149
|
const LineType = kind === "segment" ? mafs.Line.Segment : mafs.Line.ThroughPoints;
|
|
29153
|
-
|
|
29150
|
+
let arrowTip = kind === "segment" ? point2.coord : getIntersectionOfRayWithBox(point2.coord, point1.coord, range);
|
|
29151
|
+
const direction = mafs.vec.sub(point2.coord, point1.coord);
|
|
29152
|
+
let angle = calculateAngleInDegrees(direction);
|
|
29153
|
+
const startArrowHead = kind !== "segment" && /*#__PURE__*/React__namespace.createElement(Arrowhead, {
|
|
29154
|
+
angle: angle,
|
|
29155
|
+
tip: arrowTip,
|
|
29156
|
+
color: lockedFigureColors[color]
|
|
29157
|
+
});
|
|
29158
|
+
arrowTip = kind === "segment" ? point1.coord : getIntersectionOfRayWithBox(point1.coord, point2.coord, range);
|
|
29159
|
+
angle = angle > 180 ? angle - 180 : angle + 180;
|
|
29160
|
+
const endArrowHead = kind !== "segment" && /*#__PURE__*/React__namespace.createElement(Arrowhead, {
|
|
29161
|
+
angle: angle,
|
|
29162
|
+
tip: arrowTip,
|
|
29163
|
+
color: lockedFigureColors[color]
|
|
29164
|
+
});
|
|
29165
|
+
line = /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, startArrowHead, /*#__PURE__*/React__namespace.createElement(LineType, {
|
|
29154
29166
|
point1: point1.coord,
|
|
29155
29167
|
point2: point2.coord,
|
|
29156
29168
|
color: lockedFigureColors[color],
|
|
29157
29169
|
style: lineStyle
|
|
29158
|
-
});
|
|
29170
|
+
}), endArrowHead);
|
|
29159
29171
|
}
|
|
29160
29172
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29161
29173
|
className: kind === "ray" ? "locked-ray" : "locked-line"
|
|
@@ -29261,6 +29273,20 @@ function movePoint(index, destination) {
|
|
|
29261
29273
|
destination
|
|
29262
29274
|
};
|
|
29263
29275
|
}
|
|
29276
|
+
const MOVE_CENTER = "move-center";
|
|
29277
|
+
function moveCenter(destination) {
|
|
29278
|
+
return {
|
|
29279
|
+
type: MOVE_CENTER,
|
|
29280
|
+
destination
|
|
29281
|
+
};
|
|
29282
|
+
}
|
|
29283
|
+
const MOVE_RADIUS_POINT = "MOVE_RADIUS_POINT";
|
|
29284
|
+
function moveRadiusPoint(destination) {
|
|
29285
|
+
return {
|
|
29286
|
+
type: MOVE_RADIUS_POINT,
|
|
29287
|
+
destination
|
|
29288
|
+
};
|
|
29289
|
+
}
|
|
29264
29290
|
const CHANGE_SNAP_STEP = "change-snap-step";
|
|
29265
29291
|
function changeSnapStep(snapStep) {
|
|
29266
29292
|
return {
|
|
@@ -29876,6 +29902,287 @@ const PolygonGraph = props => {
|
|
|
29876
29902
|
})));
|
|
29877
29903
|
};
|
|
29878
29904
|
|
|
29905
|
+
function initializeGraphState(params) {
|
|
29906
|
+
const {
|
|
29907
|
+
graph,
|
|
29908
|
+
step,
|
|
29909
|
+
snapStep,
|
|
29910
|
+
range,
|
|
29911
|
+
markings
|
|
29912
|
+
} = params;
|
|
29913
|
+
const shared = {
|
|
29914
|
+
hasBeenInteractedWith: false,
|
|
29915
|
+
range,
|
|
29916
|
+
snapStep,
|
|
29917
|
+
markings
|
|
29918
|
+
};
|
|
29919
|
+
switch (graph.type) {
|
|
29920
|
+
case "segment":
|
|
29921
|
+
return {
|
|
29922
|
+
...shared,
|
|
29923
|
+
type: "segment",
|
|
29924
|
+
coords: getDefaultSegments({
|
|
29925
|
+
graph,
|
|
29926
|
+
step,
|
|
29927
|
+
range
|
|
29928
|
+
})
|
|
29929
|
+
};
|
|
29930
|
+
case "linear":
|
|
29931
|
+
case "linear-system":
|
|
29932
|
+
case "ray":
|
|
29933
|
+
return {
|
|
29934
|
+
...shared,
|
|
29935
|
+
type: graph.type,
|
|
29936
|
+
// Linear and ray graphs have a single tuple of points, while a
|
|
29937
|
+
// linear system has two tuples of points.
|
|
29938
|
+
coords: getLineCoords({
|
|
29939
|
+
graph,
|
|
29940
|
+
range,
|
|
29941
|
+
step
|
|
29942
|
+
})
|
|
29943
|
+
};
|
|
29944
|
+
case "polygon":
|
|
29945
|
+
return {
|
|
29946
|
+
...shared,
|
|
29947
|
+
type: "polygon",
|
|
29948
|
+
showAngles: Boolean(graph.showAngles),
|
|
29949
|
+
showSides: Boolean(graph.showSides),
|
|
29950
|
+
coords: getPolygonCoords({
|
|
29951
|
+
graph,
|
|
29952
|
+
range,
|
|
29953
|
+
step
|
|
29954
|
+
})
|
|
29955
|
+
};
|
|
29956
|
+
case "point":
|
|
29957
|
+
return {
|
|
29958
|
+
...shared,
|
|
29959
|
+
type: graph.type,
|
|
29960
|
+
coords: getDefaultPoints({
|
|
29961
|
+
graph,
|
|
29962
|
+
step,
|
|
29963
|
+
range
|
|
29964
|
+
})
|
|
29965
|
+
};
|
|
29966
|
+
case "circle":
|
|
29967
|
+
return {
|
|
29968
|
+
...shared,
|
|
29969
|
+
type: graph.type,
|
|
29970
|
+
center: [0, 0],
|
|
29971
|
+
radiusPoint: [1, 0]
|
|
29972
|
+
};
|
|
29973
|
+
case "angle":
|
|
29974
|
+
case "sinusoid":
|
|
29975
|
+
case "quadratic":
|
|
29976
|
+
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
29977
|
+
default:
|
|
29978
|
+
throw new wonderStuffCore.UnreachableCaseError(graph);
|
|
29979
|
+
}
|
|
29980
|
+
}
|
|
29981
|
+
const getDefaultPoints = _ref => {
|
|
29982
|
+
let {
|
|
29983
|
+
graph,
|
|
29984
|
+
range,
|
|
29985
|
+
step
|
|
29986
|
+
} = _ref;
|
|
29987
|
+
const numPoints = graph.numPoints || 1;
|
|
29988
|
+
let coords = graph.coords?.slice();
|
|
29989
|
+
if (coords) {
|
|
29990
|
+
return coords;
|
|
29991
|
+
}
|
|
29992
|
+
switch (numPoints) {
|
|
29993
|
+
case 1:
|
|
29994
|
+
// Back in the day, one point's coords were in graph.coord
|
|
29995
|
+
coords = [graph.coord || [0, 0]];
|
|
29996
|
+
break;
|
|
29997
|
+
case 2:
|
|
29998
|
+
coords = [[-5, 0], [5, 0]];
|
|
29999
|
+
break;
|
|
30000
|
+
case 3:
|
|
30001
|
+
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
30002
|
+
break;
|
|
30003
|
+
case 4:
|
|
30004
|
+
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
30005
|
+
break;
|
|
30006
|
+
case 5:
|
|
30007
|
+
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
30008
|
+
break;
|
|
30009
|
+
case 6:
|
|
30010
|
+
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
30011
|
+
break;
|
|
30012
|
+
default:
|
|
30013
|
+
coords = [];
|
|
30014
|
+
break;
|
|
30015
|
+
}
|
|
30016
|
+
// Transform coords from their -10 to 10 space to 0 to 1
|
|
30017
|
+
// because of the old graph.coord, and also it's easier.
|
|
30018
|
+
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
30019
|
+
return normalizePoints(range, step, newCoords);
|
|
30020
|
+
};
|
|
30021
|
+
|
|
30022
|
+
// TS v4 doesn't narrow return types, while v5 does.
|
|
30023
|
+
// Instead of updating to v5, using generic type to relate input and output types.
|
|
30024
|
+
function getGradableGraph(state, initialGraph) {
|
|
30025
|
+
if (!state.hasBeenInteractedWith) {
|
|
30026
|
+
return {
|
|
30027
|
+
...initialGraph
|
|
30028
|
+
};
|
|
30029
|
+
}
|
|
30030
|
+
if (initialGraph.type === "linear-system" && state.type === "linear-system") {
|
|
30031
|
+
return {
|
|
30032
|
+
...initialGraph,
|
|
30033
|
+
coords: state.coords
|
|
30034
|
+
};
|
|
30035
|
+
}
|
|
30036
|
+
if (state.type === "segment" && initialGraph.type === "segment") {
|
|
30037
|
+
return {
|
|
30038
|
+
...initialGraph,
|
|
30039
|
+
coords: state.coords
|
|
30040
|
+
};
|
|
30041
|
+
}
|
|
30042
|
+
if (state.type === "linear" && initialGraph.type === "linear") {
|
|
30043
|
+
return {
|
|
30044
|
+
...initialGraph,
|
|
30045
|
+
coords: state.coords[0]
|
|
30046
|
+
};
|
|
30047
|
+
}
|
|
30048
|
+
if (state.type === "ray" && initialGraph.type === "ray") {
|
|
30049
|
+
return {
|
|
30050
|
+
...initialGraph,
|
|
30051
|
+
coords: state.coords[0]
|
|
30052
|
+
};
|
|
30053
|
+
}
|
|
30054
|
+
if (state.type === "polygon" && initialGraph.type === "polygon") {
|
|
30055
|
+
return {
|
|
30056
|
+
...initialGraph,
|
|
30057
|
+
coords: state.coords
|
|
30058
|
+
};
|
|
30059
|
+
}
|
|
30060
|
+
if (state.type === "point" && initialGraph.type === "point") {
|
|
30061
|
+
return {
|
|
30062
|
+
...initialGraph,
|
|
30063
|
+
coords: state.coords
|
|
30064
|
+
};
|
|
30065
|
+
}
|
|
30066
|
+
if (state.type === "circle" && initialGraph.type === "circle") {
|
|
30067
|
+
return {
|
|
30068
|
+
...initialGraph,
|
|
30069
|
+
center: state.center,
|
|
30070
|
+
radius: getRadius(state)
|
|
30071
|
+
};
|
|
30072
|
+
}
|
|
30073
|
+
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
30074
|
+
}
|
|
30075
|
+
const getDefaultSegments = _ref2 => {
|
|
30076
|
+
let {
|
|
30077
|
+
graph,
|
|
30078
|
+
range,
|
|
30079
|
+
step
|
|
30080
|
+
} = _ref2;
|
|
30081
|
+
const ys = n => {
|
|
30082
|
+
switch (n) {
|
|
30083
|
+
case 2:
|
|
30084
|
+
return [5, -5];
|
|
30085
|
+
case 3:
|
|
30086
|
+
return [5, 0, -5];
|
|
30087
|
+
case 4:
|
|
30088
|
+
return [6, 2, -2, -6];
|
|
30089
|
+
case 5:
|
|
30090
|
+
return [6, 3, 0, -3, -6];
|
|
30091
|
+
case 6:
|
|
30092
|
+
return [5, 3, 1, -1, -3, -5];
|
|
30093
|
+
default:
|
|
30094
|
+
return [5];
|
|
30095
|
+
}
|
|
30096
|
+
};
|
|
30097
|
+
const defaultRange = [[-10, 10], [-10, 10]];
|
|
30098
|
+
return ys(graph.numSegments).map(y => {
|
|
30099
|
+
let endpoints = [[-5, y], [5, y]];
|
|
30100
|
+
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
30101
|
+
endpoints = normalizePoints(range, step, endpoints);
|
|
30102
|
+
return endpoints;
|
|
30103
|
+
});
|
|
30104
|
+
};
|
|
30105
|
+
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
30106
|
+
const getLineCoords = _ref3 => {
|
|
30107
|
+
let {
|
|
30108
|
+
graph,
|
|
30109
|
+
range,
|
|
30110
|
+
step
|
|
30111
|
+
} = _ref3;
|
|
30112
|
+
return (
|
|
30113
|
+
// Return two lines for a linear system, one for a ray or linear
|
|
30114
|
+
graph.coords ?? graph.type === "linear-system" ? defaultLinearCoords.map(collinear => normalizePoints(range, step, collinear)) : [normalizePoints(range, step, defaultLinearCoords[0])]
|
|
30115
|
+
);
|
|
30116
|
+
};
|
|
30117
|
+
const getPolygonCoords = _ref4 => {
|
|
30118
|
+
let {
|
|
30119
|
+
graph,
|
|
30120
|
+
range,
|
|
30121
|
+
step
|
|
30122
|
+
} = _ref4;
|
|
30123
|
+
let coords = graph.coords?.slice();
|
|
30124
|
+
if (coords) {
|
|
30125
|
+
return coords;
|
|
30126
|
+
}
|
|
30127
|
+
const n = graph.numSides || 3;
|
|
30128
|
+
if (n === "unlimited") {
|
|
30129
|
+
coords = [];
|
|
30130
|
+
} else {
|
|
30131
|
+
const angle = 2 * Math.PI / n;
|
|
30132
|
+
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
30133
|
+
|
|
30134
|
+
// TODO(alex): Generalize this to more than just triangles so that
|
|
30135
|
+
// all polygons have whole number side lengths if snapping to sides
|
|
30136
|
+
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
30137
|
+
|
|
30138
|
+
// Generate coords of a regular polygon with n sides
|
|
30139
|
+
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
30140
|
+
}
|
|
30141
|
+
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
30142
|
+
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
30143
|
+
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
30144
|
+
return coords;
|
|
30145
|
+
};
|
|
30146
|
+
|
|
30147
|
+
/**
|
|
30148
|
+
* determine radius of a circle graph
|
|
30149
|
+
*
|
|
30150
|
+
* @param graph - the graph object containing the circle
|
|
30151
|
+
* @returns the radius of the circle
|
|
30152
|
+
*/
|
|
30153
|
+
function getRadius(graph) {
|
|
30154
|
+
const [centerX, centerY] = graph.center;
|
|
30155
|
+
const [edgeX, edgeY] = graph.radiusPoint;
|
|
30156
|
+
return Math.sqrt(Math.pow(edgeX - centerX, 2) + Math.pow(edgeY - centerY, 2));
|
|
30157
|
+
}
|
|
30158
|
+
|
|
30159
|
+
function CircleGraph(props) {
|
|
30160
|
+
const {
|
|
30161
|
+
dispatch,
|
|
30162
|
+
graphState
|
|
30163
|
+
} = props;
|
|
30164
|
+
const {
|
|
30165
|
+
center,
|
|
30166
|
+
radiusPoint
|
|
30167
|
+
} = graphState;
|
|
30168
|
+
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Circle, {
|
|
30169
|
+
center: center,
|
|
30170
|
+
radius: getRadius(graphState),
|
|
30171
|
+
fillOpacity: 0,
|
|
30172
|
+
color: wonderBlocksTokens.color.blue
|
|
30173
|
+
}), /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
|
|
30174
|
+
point: center,
|
|
30175
|
+
onMove: newCenter => {
|
|
30176
|
+
dispatch(moveCenter(newCenter));
|
|
30177
|
+
}
|
|
30178
|
+
}), /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
|
|
30179
|
+
point: radiusPoint,
|
|
30180
|
+
onMove: newRadiusPoint => {
|
|
30181
|
+
dispatch(moveRadiusPoint(newRadiusPoint));
|
|
30182
|
+
}
|
|
30183
|
+
}));
|
|
30184
|
+
}
|
|
30185
|
+
|
|
29879
30186
|
function PointGraph(props) {
|
|
29880
30187
|
const {
|
|
29881
30188
|
dispatch
|
|
@@ -30681,6 +30988,10 @@ function interactiveGraphReducer(state, action) {
|
|
|
30681
30988
|
return doMoveAll(state, action);
|
|
30682
30989
|
case MOVE_POINT:
|
|
30683
30990
|
return doMovePoint(state, action);
|
|
30991
|
+
case MOVE_CENTER:
|
|
30992
|
+
return doMoveCenter(state, action);
|
|
30993
|
+
case MOVE_RADIUS_POINT:
|
|
30994
|
+
return doMoveRadiusPoint(state, action);
|
|
30684
30995
|
case CHANGE_SNAP_STEP:
|
|
30685
30996
|
return doChangeSnapStep(state, action);
|
|
30686
30997
|
case CHANGE_RANGE:
|
|
@@ -30820,6 +31131,67 @@ function doMovePoint(state, action) {
|
|
|
30820
31131
|
throw new Error("The movePoint action is only for point and polygon graphs");
|
|
30821
31132
|
}
|
|
30822
31133
|
}
|
|
31134
|
+
function doMoveCenter(state, action) {
|
|
31135
|
+
switch (state.type) {
|
|
31136
|
+
case "circle":
|
|
31137
|
+
{
|
|
31138
|
+
// Constrain the center of the circle to the chart range
|
|
31139
|
+
const constrainedCenter = bound$1({
|
|
31140
|
+
snapStep: state.snapStep,
|
|
31141
|
+
range: state.range,
|
|
31142
|
+
point: action.destination
|
|
31143
|
+
});
|
|
31144
|
+
|
|
31145
|
+
// Reposition the radius point based on the new center
|
|
31146
|
+
// (spread to make sure we're not going to mutate anything)
|
|
31147
|
+
const newRadiusPoint = [...mafs.vec.add(state.radiusPoint, mafs.vec.sub(constrainedCenter, state.center))];
|
|
31148
|
+
|
|
31149
|
+
// Try to position the radius handle in a visible spot
|
|
31150
|
+
// if it otherwise would be off the chart
|
|
31151
|
+
// ex: if the handle is on the right and we move the center
|
|
31152
|
+
// to the rightmost position, move the handle to the left
|
|
31153
|
+
const [xMin, xMax] = state.range[0];
|
|
31154
|
+
const [radX] = newRadiusPoint;
|
|
31155
|
+
if (radX < xMin || radX > xMax) {
|
|
31156
|
+
const xJumpDist = (radX - constrainedCenter[0]) * 2;
|
|
31157
|
+
const possibleNewX = radX - xJumpDist;
|
|
31158
|
+
if (possibleNewX >= xMin && possibleNewX <= xMax) {
|
|
31159
|
+
newRadiusPoint[0] = possibleNewX;
|
|
31160
|
+
}
|
|
31161
|
+
}
|
|
31162
|
+
return {
|
|
31163
|
+
...state,
|
|
31164
|
+
hasBeenInteractedWith: true,
|
|
31165
|
+
center: constrainedCenter,
|
|
31166
|
+
radiusPoint: newRadiusPoint
|
|
31167
|
+
};
|
|
31168
|
+
}
|
|
31169
|
+
default:
|
|
31170
|
+
throw new Error("The doMoveCenter action is only for circle graphs");
|
|
31171
|
+
}
|
|
31172
|
+
}
|
|
31173
|
+
function doMoveRadiusPoint(state, action) {
|
|
31174
|
+
switch (state.type) {
|
|
31175
|
+
case "circle":
|
|
31176
|
+
{
|
|
31177
|
+
const [xMin, xMax] = state.range[0];
|
|
31178
|
+
const nextRadiusPoint = [
|
|
31179
|
+
// Constrain to graph range
|
|
31180
|
+
// The +0 is to convert -0 to +0
|
|
31181
|
+
Math.min(Math.max(xMin, action.destination[0] + 0), xMax), state.center[1]];
|
|
31182
|
+
if (___default["default"].isEqual(nextRadiusPoint, state.center)) {
|
|
31183
|
+
return state;
|
|
31184
|
+
}
|
|
31185
|
+
return {
|
|
31186
|
+
...state,
|
|
31187
|
+
hasBeenInteractedWith: true,
|
|
31188
|
+
radiusPoint: nextRadiusPoint
|
|
31189
|
+
};
|
|
31190
|
+
}
|
|
31191
|
+
default:
|
|
31192
|
+
throw new Error("The doMoveRadiusPoint action is only for circle graphs");
|
|
31193
|
+
}
|
|
31194
|
+
}
|
|
30823
31195
|
function doChangeSnapStep(state, action) {
|
|
30824
31196
|
if (
|
|
30825
31197
|
// Deep equality check since these are arrays
|
|
@@ -30938,235 +31310,6 @@ function setAtIndex(args) {
|
|
|
30938
31310
|
return copy;
|
|
30939
31311
|
}
|
|
30940
31312
|
|
|
30941
|
-
function initializeGraphState(params) {
|
|
30942
|
-
const {
|
|
30943
|
-
graph,
|
|
30944
|
-
step,
|
|
30945
|
-
snapStep,
|
|
30946
|
-
range,
|
|
30947
|
-
markings
|
|
30948
|
-
} = params;
|
|
30949
|
-
const shared = {
|
|
30950
|
-
hasBeenInteractedWith: false,
|
|
30951
|
-
range,
|
|
30952
|
-
snapStep,
|
|
30953
|
-
markings
|
|
30954
|
-
};
|
|
30955
|
-
switch (graph.type) {
|
|
30956
|
-
case "segment":
|
|
30957
|
-
return {
|
|
30958
|
-
...shared,
|
|
30959
|
-
type: "segment",
|
|
30960
|
-
coords: getDefaultSegments({
|
|
30961
|
-
graph,
|
|
30962
|
-
step,
|
|
30963
|
-
range
|
|
30964
|
-
})
|
|
30965
|
-
};
|
|
30966
|
-
case "linear":
|
|
30967
|
-
case "linear-system":
|
|
30968
|
-
case "ray":
|
|
30969
|
-
return {
|
|
30970
|
-
...shared,
|
|
30971
|
-
type: graph.type,
|
|
30972
|
-
// Linear and ray graphs have a single tuple of points, while a
|
|
30973
|
-
// linear system has two tuples of points.
|
|
30974
|
-
coords: getLineCoords({
|
|
30975
|
-
graph,
|
|
30976
|
-
range,
|
|
30977
|
-
step
|
|
30978
|
-
})
|
|
30979
|
-
};
|
|
30980
|
-
case "polygon":
|
|
30981
|
-
return {
|
|
30982
|
-
...shared,
|
|
30983
|
-
type: "polygon",
|
|
30984
|
-
showAngles: Boolean(graph.showAngles),
|
|
30985
|
-
showSides: Boolean(graph.showSides),
|
|
30986
|
-
coords: getPolygonCoords({
|
|
30987
|
-
graph,
|
|
30988
|
-
range,
|
|
30989
|
-
step
|
|
30990
|
-
})
|
|
30991
|
-
};
|
|
30992
|
-
case "point":
|
|
30993
|
-
return {
|
|
30994
|
-
...shared,
|
|
30995
|
-
type: graph.type,
|
|
30996
|
-
coords: getDefaultPoints({
|
|
30997
|
-
graph,
|
|
30998
|
-
step,
|
|
30999
|
-
range
|
|
31000
|
-
})
|
|
31001
|
-
};
|
|
31002
|
-
case "angle":
|
|
31003
|
-
case "circle":
|
|
31004
|
-
case "sinusoid":
|
|
31005
|
-
case "quadratic":
|
|
31006
|
-
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
31007
|
-
default:
|
|
31008
|
-
throw new wonderStuffCore.UnreachableCaseError(graph);
|
|
31009
|
-
}
|
|
31010
|
-
}
|
|
31011
|
-
const getDefaultPoints = _ref => {
|
|
31012
|
-
let {
|
|
31013
|
-
graph,
|
|
31014
|
-
range,
|
|
31015
|
-
step
|
|
31016
|
-
} = _ref;
|
|
31017
|
-
const numPoints = graph.numPoints || 1;
|
|
31018
|
-
let coords = graph.coords?.slice();
|
|
31019
|
-
if (coords) {
|
|
31020
|
-
return coords;
|
|
31021
|
-
}
|
|
31022
|
-
switch (numPoints) {
|
|
31023
|
-
case 1:
|
|
31024
|
-
// Back in the day, one point's coords were in graph.coord
|
|
31025
|
-
coords = [graph.coord || [0, 0]];
|
|
31026
|
-
break;
|
|
31027
|
-
case 2:
|
|
31028
|
-
coords = [[-5, 0], [5, 0]];
|
|
31029
|
-
break;
|
|
31030
|
-
case 3:
|
|
31031
|
-
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
31032
|
-
break;
|
|
31033
|
-
case 4:
|
|
31034
|
-
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
31035
|
-
break;
|
|
31036
|
-
case 5:
|
|
31037
|
-
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
31038
|
-
break;
|
|
31039
|
-
case 6:
|
|
31040
|
-
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
31041
|
-
break;
|
|
31042
|
-
default:
|
|
31043
|
-
coords = [];
|
|
31044
|
-
break;
|
|
31045
|
-
}
|
|
31046
|
-
// Transform coords from their -10 to 10 space to 0 to 1
|
|
31047
|
-
// because of the old graph.coord, and also it's easier.
|
|
31048
|
-
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
31049
|
-
return normalizePoints(range, step, newCoords);
|
|
31050
|
-
};
|
|
31051
|
-
|
|
31052
|
-
// TS v4 doesn't narrow return types, while v5 does.
|
|
31053
|
-
// Instead of updating to v5, using generic type to relate input and output types.
|
|
31054
|
-
function getGradableGraph(state, initialGraph) {
|
|
31055
|
-
if (!state.hasBeenInteractedWith) {
|
|
31056
|
-
return {
|
|
31057
|
-
...initialGraph
|
|
31058
|
-
};
|
|
31059
|
-
}
|
|
31060
|
-
if (initialGraph.type === "linear-system" && state.type === "linear-system") {
|
|
31061
|
-
return {
|
|
31062
|
-
...initialGraph,
|
|
31063
|
-
coords: state.coords
|
|
31064
|
-
};
|
|
31065
|
-
}
|
|
31066
|
-
if (state.type === "segment" && initialGraph.type === "segment") {
|
|
31067
|
-
return {
|
|
31068
|
-
...initialGraph,
|
|
31069
|
-
coords: state.coords
|
|
31070
|
-
};
|
|
31071
|
-
}
|
|
31072
|
-
if (state.type === "linear" && initialGraph.type === "linear") {
|
|
31073
|
-
return {
|
|
31074
|
-
...initialGraph,
|
|
31075
|
-
coords: state.coords[0]
|
|
31076
|
-
};
|
|
31077
|
-
}
|
|
31078
|
-
if (state.type === "ray" && initialGraph.type === "ray") {
|
|
31079
|
-
return {
|
|
31080
|
-
...initialGraph,
|
|
31081
|
-
coords: state.coords[0]
|
|
31082
|
-
};
|
|
31083
|
-
}
|
|
31084
|
-
if (state.type === "polygon" && initialGraph.type === "polygon") {
|
|
31085
|
-
return {
|
|
31086
|
-
...initialGraph,
|
|
31087
|
-
coords: state.coords
|
|
31088
|
-
};
|
|
31089
|
-
}
|
|
31090
|
-
if (state.type === "point" && initialGraph.type === "point") {
|
|
31091
|
-
return {
|
|
31092
|
-
...initialGraph,
|
|
31093
|
-
coords: state.coords
|
|
31094
|
-
};
|
|
31095
|
-
}
|
|
31096
|
-
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
31097
|
-
}
|
|
31098
|
-
const getDefaultSegments = _ref2 => {
|
|
31099
|
-
let {
|
|
31100
|
-
graph,
|
|
31101
|
-
range,
|
|
31102
|
-
step
|
|
31103
|
-
} = _ref2;
|
|
31104
|
-
const ys = n => {
|
|
31105
|
-
switch (n) {
|
|
31106
|
-
case 2:
|
|
31107
|
-
return [5, -5];
|
|
31108
|
-
case 3:
|
|
31109
|
-
return [5, 0, -5];
|
|
31110
|
-
case 4:
|
|
31111
|
-
return [6, 2, -2, -6];
|
|
31112
|
-
case 5:
|
|
31113
|
-
return [6, 3, 0, -3, -6];
|
|
31114
|
-
case 6:
|
|
31115
|
-
return [5, 3, 1, -1, -3, -5];
|
|
31116
|
-
default:
|
|
31117
|
-
return [5];
|
|
31118
|
-
}
|
|
31119
|
-
};
|
|
31120
|
-
const defaultRange = [[-10, 10], [-10, 10]];
|
|
31121
|
-
return ys(graph.numSegments).map(y => {
|
|
31122
|
-
let endpoints = [[-5, y], [5, y]];
|
|
31123
|
-
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
31124
|
-
endpoints = normalizePoints(range, step, endpoints);
|
|
31125
|
-
return endpoints;
|
|
31126
|
-
});
|
|
31127
|
-
};
|
|
31128
|
-
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
31129
|
-
const getLineCoords = _ref3 => {
|
|
31130
|
-
let {
|
|
31131
|
-
graph,
|
|
31132
|
-
range,
|
|
31133
|
-
step
|
|
31134
|
-
} = _ref3;
|
|
31135
|
-
return (
|
|
31136
|
-
// Return two lines for a linear system, one for a ray or linear
|
|
31137
|
-
graph.coords ?? graph.type === "linear-system" ? defaultLinearCoords.map(collinear => normalizePoints(range, step, collinear)) : [normalizePoints(range, step, defaultLinearCoords[0])]
|
|
31138
|
-
);
|
|
31139
|
-
};
|
|
31140
|
-
const getPolygonCoords = _ref4 => {
|
|
31141
|
-
let {
|
|
31142
|
-
graph,
|
|
31143
|
-
range,
|
|
31144
|
-
step
|
|
31145
|
-
} = _ref4;
|
|
31146
|
-
let coords = graph.coords?.slice();
|
|
31147
|
-
if (coords) {
|
|
31148
|
-
return coords;
|
|
31149
|
-
}
|
|
31150
|
-
const n = graph.numSides || 3;
|
|
31151
|
-
if (n === "unlimited") {
|
|
31152
|
-
coords = [];
|
|
31153
|
-
} else {
|
|
31154
|
-
const angle = 2 * Math.PI / n;
|
|
31155
|
-
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
31156
|
-
|
|
31157
|
-
// TODO(alex): Generalize this to more than just triangles so that
|
|
31158
|
-
// all polygons have whole number side lengths if snapping to sides
|
|
31159
|
-
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
31160
|
-
|
|
31161
|
-
// Generate coords of a regular polygon with n sides
|
|
31162
|
-
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
31163
|
-
}
|
|
31164
|
-
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
31165
|
-
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
31166
|
-
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
31167
|
-
return coords;
|
|
31168
|
-
};
|
|
31169
|
-
|
|
31170
31313
|
const renderGraph = props => {
|
|
31171
31314
|
const {
|
|
31172
31315
|
state,
|
|
@@ -31203,20 +31346,46 @@ const renderGraph = props => {
|
|
|
31203
31346
|
dispatch: dispatch
|
|
31204
31347
|
});
|
|
31205
31348
|
case "circle":
|
|
31206
|
-
|
|
31349
|
+
return /*#__PURE__*/React__namespace.createElement(CircleGraph, {
|
|
31350
|
+
graphState: state,
|
|
31351
|
+
dispatch: dispatch
|
|
31352
|
+
});
|
|
31207
31353
|
default:
|
|
31208
31354
|
return new wonderStuffCore.UnreachableCaseError(type);
|
|
31209
31355
|
}
|
|
31210
31356
|
};
|
|
31357
|
+
|
|
31358
|
+
// Rather than be tightly bound to how data was structured in
|
|
31359
|
+
// the legacy interactive graph, this lets us store state
|
|
31360
|
+
// however we want and we just transform it before handing it off
|
|
31361
|
+
// the the parent InteractiveGraph
|
|
31362
|
+
function mafsStateToInteractiveGraph(state) {
|
|
31363
|
+
if (state.graph.type === "circle") {
|
|
31364
|
+
return {
|
|
31365
|
+
...state,
|
|
31366
|
+
graph: {
|
|
31367
|
+
...state.graph,
|
|
31368
|
+
radius: getRadius(state.graph)
|
|
31369
|
+
}
|
|
31370
|
+
};
|
|
31371
|
+
}
|
|
31372
|
+
return {
|
|
31373
|
+
...state
|
|
31374
|
+
};
|
|
31375
|
+
}
|
|
31211
31376
|
const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
|
|
31212
31377
|
const [state, dispatch] = React__namespace.useReducer(interactiveGraphReducer, props, initializeGraphState);
|
|
31213
31378
|
React.useImperativeHandle(ref, () => ({
|
|
31214
31379
|
getUserInput: () => getGradableGraph(state, props.graph)
|
|
31215
31380
|
}));
|
|
31216
|
-
|
|
31381
|
+
function onChange(next) {
|
|
31382
|
+
props.onChange(mafsStateToInteractiveGraph(next));
|
|
31383
|
+
}
|
|
31384
|
+
return /*#__PURE__*/React__namespace.createElement(MafsGraph, _extends({}, props, {
|
|
31217
31385
|
state: state,
|
|
31218
|
-
dispatch: dispatch
|
|
31219
|
-
|
|
31386
|
+
dispatch: dispatch,
|
|
31387
|
+
onChange: onChange
|
|
31388
|
+
}));
|
|
31220
31389
|
});
|
|
31221
31390
|
const MafsGraph = props => {
|
|
31222
31391
|
const {
|
|
@@ -31258,6 +31427,7 @@ const MafsGraph = props => {
|
|
|
31258
31427
|
labels
|
|
31259
31428
|
}
|
|
31260
31429
|
}, /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
31430
|
+
className: "mafs-graph",
|
|
31261
31431
|
style: {
|
|
31262
31432
|
width,
|
|
31263
31433
|
height,
|
|
@@ -43389,7 +43559,7 @@ const init = function (options) {
|
|
|
43389
43559
|
|
|
43390
43560
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
43391
43561
|
const libName = "@khanacademy/perseus";
|
|
43392
|
-
const libVersion = "22.
|
|
43562
|
+
const libVersion = "22.4.0";
|
|
43393
43563
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
43394
43564
|
|
|
43395
43565
|
const apiVersion = {
|