@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/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');
|
|
@@ -29012,6 +29012,7 @@ const arrowPath = pathBuilder().move(-3, 4).curve(-2.75, 2.5, 0, 0.25, 0.75, 0).
|
|
|
29012
29012
|
function Arrowhead(props) {
|
|
29013
29013
|
const [point] = useTransformVectorsToPixels(props.tip);
|
|
29014
29014
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29015
|
+
className: "interactive-graph-arrowhead",
|
|
29015
29016
|
transform: `translate(${point[0]} ${point[1]}) rotate(${-props.angle})`
|
|
29016
29017
|
}, /*#__PURE__*/React__namespace.createElement("g", {
|
|
29017
29018
|
transform: "translate(-1.5)"
|
|
@@ -29026,49 +29027,7 @@ function Arrowhead(props) {
|
|
|
29026
29027
|
})));
|
|
29027
29028
|
}
|
|
29028
29029
|
|
|
29029
|
-
function
|
|
29030
|
-
const {
|
|
29031
|
-
start,
|
|
29032
|
-
end,
|
|
29033
|
-
style,
|
|
29034
|
-
className,
|
|
29035
|
-
testId
|
|
29036
|
-
} = props;
|
|
29037
|
-
return /*#__PURE__*/React__namespace.createElement("line", {
|
|
29038
|
-
x1: start[0],
|
|
29039
|
-
y1: start[1],
|
|
29040
|
-
x2: end[0],
|
|
29041
|
-
y2: end[1],
|
|
29042
|
-
style: style,
|
|
29043
|
-
className: className,
|
|
29044
|
-
"data-testid": testId
|
|
29045
|
-
});
|
|
29046
|
-
}
|
|
29047
|
-
|
|
29048
|
-
function Vector(props) {
|
|
29049
|
-
const {
|
|
29050
|
-
tail,
|
|
29051
|
-
tip,
|
|
29052
|
-
color,
|
|
29053
|
-
style
|
|
29054
|
-
} = props;
|
|
29055
|
-
const [tailPx, tipPx] = useTransformVectorsToPixels(tail, tip);
|
|
29056
|
-
const direction = mafs.vec.sub(tip, tail);
|
|
29057
|
-
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29058
|
-
style: {
|
|
29059
|
-
stroke: color,
|
|
29060
|
-
strokeWidth: 2
|
|
29061
|
-
}
|
|
29062
|
-
}, /*#__PURE__*/React__namespace.createElement(SVGLine, {
|
|
29063
|
-
start: tailPx,
|
|
29064
|
-
end: tipPx,
|
|
29065
|
-
style: style
|
|
29066
|
-
}), /*#__PURE__*/React__namespace.createElement(Arrowhead, {
|
|
29067
|
-
tip: tip,
|
|
29068
|
-
angle: angleDegrees(direction)
|
|
29069
|
-
}));
|
|
29070
|
-
}
|
|
29071
|
-
function angleDegrees(_ref) {
|
|
29030
|
+
function calculateAngleInDegrees(_ref) {
|
|
29072
29031
|
let [x, y] = _ref;
|
|
29073
29032
|
return Math.atan2(y, x) * 180 / Math.PI;
|
|
29074
29033
|
}
|
|
@@ -29111,10 +29070,57 @@ const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
|
|
|
29111
29070
|
return [xMax, yAtXMax];
|
|
29112
29071
|
}
|
|
29113
29072
|
};
|
|
29114
|
-
const getLines = points =>
|
|
29115
|
-
|
|
29116
|
-
|
|
29117
|
-
|
|
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
|
+
}
|
|
29118
29124
|
|
|
29119
29125
|
const LockedLine = props => {
|
|
29120
29126
|
const {
|
|
@@ -29130,27 +29136,38 @@ const LockedLine = props => {
|
|
|
29130
29136
|
let line;
|
|
29131
29137
|
if (kind === "ray") {
|
|
29132
29138
|
// Rays extend to the end of the graph in one direction.
|
|
29133
|
-
const
|
|
29139
|
+
const extendedPoint = getIntersectionOfRayWithBox(point2.coord, point1.coord, range);
|
|
29134
29140
|
line = /*#__PURE__*/React__namespace.createElement(Vector, {
|
|
29135
29141
|
tail: point1.coord,
|
|
29136
|
-
tip:
|
|
29142
|
+
tip: extendedPoint,
|
|
29137
29143
|
color: lockedFigureColors[color],
|
|
29138
29144
|
style: {
|
|
29139
|
-
strokeDasharray: lineStyle === "dashed" ?
|
|
29140
|
-
// TODO(lems-1930): Uncomment this line when the
|
|
29141
|
-
// dashed style is updated in Mafs.
|
|
29142
|
-
// ? "var(--mafs-line-stroke-dash-style)"
|
|
29143
|
-
"4, 3" : undefined
|
|
29145
|
+
strokeDasharray: lineStyle === "dashed" ? "var(--mafs-line-stroke-dash-style)" : undefined
|
|
29144
29146
|
}
|
|
29145
29147
|
});
|
|
29146
29148
|
} else {
|
|
29147
29149
|
const LineType = kind === "segment" ? mafs.Line.Segment : mafs.Line.ThroughPoints;
|
|
29148
|
-
|
|
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, {
|
|
29149
29166
|
point1: point1.coord,
|
|
29150
29167
|
point2: point2.coord,
|
|
29151
29168
|
color: lockedFigureColors[color],
|
|
29152
29169
|
style: lineStyle
|
|
29153
|
-
});
|
|
29170
|
+
}), endArrowHead);
|
|
29154
29171
|
}
|
|
29155
29172
|
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29156
29173
|
className: kind === "ray" ? "locked-ray" : "locked-line"
|
|
@@ -29256,6 +29273,20 @@ function movePoint(index, destination) {
|
|
|
29256
29273
|
destination
|
|
29257
29274
|
};
|
|
29258
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
|
+
}
|
|
29259
29290
|
const CHANGE_SNAP_STEP = "change-snap-step";
|
|
29260
29291
|
function changeSnapStep(snapStep) {
|
|
29261
29292
|
return {
|
|
@@ -29871,6 +29902,287 @@ const PolygonGraph = props => {
|
|
|
29871
29902
|
})));
|
|
29872
29903
|
};
|
|
29873
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
|
+
|
|
29874
30186
|
function PointGraph(props) {
|
|
29875
30187
|
const {
|
|
29876
30188
|
dispatch
|
|
@@ -30676,6 +30988,10 @@ function interactiveGraphReducer(state, action) {
|
|
|
30676
30988
|
return doMoveAll(state, action);
|
|
30677
30989
|
case MOVE_POINT:
|
|
30678
30990
|
return doMovePoint(state, action);
|
|
30991
|
+
case MOVE_CENTER:
|
|
30992
|
+
return doMoveCenter(state, action);
|
|
30993
|
+
case MOVE_RADIUS_POINT:
|
|
30994
|
+
return doMoveRadiusPoint(state, action);
|
|
30679
30995
|
case CHANGE_SNAP_STEP:
|
|
30680
30996
|
return doChangeSnapStep(state, action);
|
|
30681
30997
|
case CHANGE_RANGE:
|
|
@@ -30815,6 +31131,67 @@ function doMovePoint(state, action) {
|
|
|
30815
31131
|
throw new Error("The movePoint action is only for point and polygon graphs");
|
|
30816
31132
|
}
|
|
30817
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
|
+
}
|
|
30818
31195
|
function doChangeSnapStep(state, action) {
|
|
30819
31196
|
if (
|
|
30820
31197
|
// Deep equality check since these are arrays
|
|
@@ -30933,235 +31310,6 @@ function setAtIndex(args) {
|
|
|
30933
31310
|
return copy;
|
|
30934
31311
|
}
|
|
30935
31312
|
|
|
30936
|
-
function initializeGraphState(params) {
|
|
30937
|
-
const {
|
|
30938
|
-
graph,
|
|
30939
|
-
step,
|
|
30940
|
-
snapStep,
|
|
30941
|
-
range,
|
|
30942
|
-
markings
|
|
30943
|
-
} = params;
|
|
30944
|
-
const shared = {
|
|
30945
|
-
hasBeenInteractedWith: false,
|
|
30946
|
-
range,
|
|
30947
|
-
snapStep,
|
|
30948
|
-
markings
|
|
30949
|
-
};
|
|
30950
|
-
switch (graph.type) {
|
|
30951
|
-
case "segment":
|
|
30952
|
-
return {
|
|
30953
|
-
...shared,
|
|
30954
|
-
type: "segment",
|
|
30955
|
-
coords: getDefaultSegments({
|
|
30956
|
-
graph,
|
|
30957
|
-
step,
|
|
30958
|
-
range
|
|
30959
|
-
})
|
|
30960
|
-
};
|
|
30961
|
-
case "linear":
|
|
30962
|
-
case "linear-system":
|
|
30963
|
-
case "ray":
|
|
30964
|
-
return {
|
|
30965
|
-
...shared,
|
|
30966
|
-
type: graph.type,
|
|
30967
|
-
// Linear and ray graphs have a single tuple of points, while a
|
|
30968
|
-
// linear system has two tuples of points.
|
|
30969
|
-
coords: getLineCoords({
|
|
30970
|
-
graph,
|
|
30971
|
-
range,
|
|
30972
|
-
step
|
|
30973
|
-
})
|
|
30974
|
-
};
|
|
30975
|
-
case "polygon":
|
|
30976
|
-
return {
|
|
30977
|
-
...shared,
|
|
30978
|
-
type: "polygon",
|
|
30979
|
-
showAngles: Boolean(graph.showAngles),
|
|
30980
|
-
showSides: Boolean(graph.showSides),
|
|
30981
|
-
coords: getPolygonCoords({
|
|
30982
|
-
graph,
|
|
30983
|
-
range,
|
|
30984
|
-
step
|
|
30985
|
-
})
|
|
30986
|
-
};
|
|
30987
|
-
case "point":
|
|
30988
|
-
return {
|
|
30989
|
-
...shared,
|
|
30990
|
-
type: graph.type,
|
|
30991
|
-
coords: getDefaultPoints({
|
|
30992
|
-
graph,
|
|
30993
|
-
step,
|
|
30994
|
-
range
|
|
30995
|
-
})
|
|
30996
|
-
};
|
|
30997
|
-
case "angle":
|
|
30998
|
-
case "circle":
|
|
30999
|
-
case "sinusoid":
|
|
31000
|
-
case "quadratic":
|
|
31001
|
-
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
31002
|
-
default:
|
|
31003
|
-
throw new wonderStuffCore.UnreachableCaseError(graph);
|
|
31004
|
-
}
|
|
31005
|
-
}
|
|
31006
|
-
const getDefaultPoints = _ref => {
|
|
31007
|
-
let {
|
|
31008
|
-
graph,
|
|
31009
|
-
range,
|
|
31010
|
-
step
|
|
31011
|
-
} = _ref;
|
|
31012
|
-
const numPoints = graph.numPoints || 1;
|
|
31013
|
-
let coords = graph.coords?.slice();
|
|
31014
|
-
if (coords) {
|
|
31015
|
-
return coords;
|
|
31016
|
-
}
|
|
31017
|
-
switch (numPoints) {
|
|
31018
|
-
case 1:
|
|
31019
|
-
// Back in the day, one point's coords were in graph.coord
|
|
31020
|
-
coords = [graph.coord || [0, 0]];
|
|
31021
|
-
break;
|
|
31022
|
-
case 2:
|
|
31023
|
-
coords = [[-5, 0], [5, 0]];
|
|
31024
|
-
break;
|
|
31025
|
-
case 3:
|
|
31026
|
-
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
31027
|
-
break;
|
|
31028
|
-
case 4:
|
|
31029
|
-
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
31030
|
-
break;
|
|
31031
|
-
case 5:
|
|
31032
|
-
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
31033
|
-
break;
|
|
31034
|
-
case 6:
|
|
31035
|
-
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
31036
|
-
break;
|
|
31037
|
-
default:
|
|
31038
|
-
coords = [];
|
|
31039
|
-
break;
|
|
31040
|
-
}
|
|
31041
|
-
// Transform coords from their -10 to 10 space to 0 to 1
|
|
31042
|
-
// because of the old graph.coord, and also it's easier.
|
|
31043
|
-
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
31044
|
-
return normalizePoints(range, step, newCoords);
|
|
31045
|
-
};
|
|
31046
|
-
|
|
31047
|
-
// TS v4 doesn't narrow return types, while v5 does.
|
|
31048
|
-
// Instead of updating to v5, using generic type to relate input and output types.
|
|
31049
|
-
function getGradableGraph(state, initialGraph) {
|
|
31050
|
-
if (!state.hasBeenInteractedWith) {
|
|
31051
|
-
return {
|
|
31052
|
-
...initialGraph
|
|
31053
|
-
};
|
|
31054
|
-
}
|
|
31055
|
-
if (initialGraph.type === "linear-system" && state.type === "linear-system") {
|
|
31056
|
-
return {
|
|
31057
|
-
...initialGraph,
|
|
31058
|
-
coords: state.coords
|
|
31059
|
-
};
|
|
31060
|
-
}
|
|
31061
|
-
if (state.type === "segment" && initialGraph.type === "segment") {
|
|
31062
|
-
return {
|
|
31063
|
-
...initialGraph,
|
|
31064
|
-
coords: state.coords
|
|
31065
|
-
};
|
|
31066
|
-
}
|
|
31067
|
-
if (state.type === "linear" && initialGraph.type === "linear") {
|
|
31068
|
-
return {
|
|
31069
|
-
...initialGraph,
|
|
31070
|
-
coords: state.coords[0]
|
|
31071
|
-
};
|
|
31072
|
-
}
|
|
31073
|
-
if (state.type === "ray" && initialGraph.type === "ray") {
|
|
31074
|
-
return {
|
|
31075
|
-
...initialGraph,
|
|
31076
|
-
coords: state.coords[0]
|
|
31077
|
-
};
|
|
31078
|
-
}
|
|
31079
|
-
if (state.type === "polygon" && initialGraph.type === "polygon") {
|
|
31080
|
-
return {
|
|
31081
|
-
...initialGraph,
|
|
31082
|
-
coords: state.coords
|
|
31083
|
-
};
|
|
31084
|
-
}
|
|
31085
|
-
if (state.type === "point" && initialGraph.type === "point") {
|
|
31086
|
-
return {
|
|
31087
|
-
...initialGraph,
|
|
31088
|
-
coords: state.coords
|
|
31089
|
-
};
|
|
31090
|
-
}
|
|
31091
|
-
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
31092
|
-
}
|
|
31093
|
-
const getDefaultSegments = _ref2 => {
|
|
31094
|
-
let {
|
|
31095
|
-
graph,
|
|
31096
|
-
range,
|
|
31097
|
-
step
|
|
31098
|
-
} = _ref2;
|
|
31099
|
-
const ys = n => {
|
|
31100
|
-
switch (n) {
|
|
31101
|
-
case 2:
|
|
31102
|
-
return [5, -5];
|
|
31103
|
-
case 3:
|
|
31104
|
-
return [5, 0, -5];
|
|
31105
|
-
case 4:
|
|
31106
|
-
return [6, 2, -2, -6];
|
|
31107
|
-
case 5:
|
|
31108
|
-
return [6, 3, 0, -3, -6];
|
|
31109
|
-
case 6:
|
|
31110
|
-
return [5, 3, 1, -1, -3, -5];
|
|
31111
|
-
default:
|
|
31112
|
-
return [5];
|
|
31113
|
-
}
|
|
31114
|
-
};
|
|
31115
|
-
const defaultRange = [[-10, 10], [-10, 10]];
|
|
31116
|
-
return ys(graph.numSegments).map(y => {
|
|
31117
|
-
let endpoints = [[-5, y], [5, y]];
|
|
31118
|
-
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
31119
|
-
endpoints = normalizePoints(range, step, endpoints);
|
|
31120
|
-
return endpoints;
|
|
31121
|
-
});
|
|
31122
|
-
};
|
|
31123
|
-
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
31124
|
-
const getLineCoords = _ref3 => {
|
|
31125
|
-
let {
|
|
31126
|
-
graph,
|
|
31127
|
-
range,
|
|
31128
|
-
step
|
|
31129
|
-
} = _ref3;
|
|
31130
|
-
return (
|
|
31131
|
-
// Return two lines for a linear system, one for a ray or linear
|
|
31132
|
-
graph.coords ?? graph.type === "linear-system" ? defaultLinearCoords.map(collinear => normalizePoints(range, step, collinear)) : [normalizePoints(range, step, defaultLinearCoords[0])]
|
|
31133
|
-
);
|
|
31134
|
-
};
|
|
31135
|
-
const getPolygonCoords = _ref4 => {
|
|
31136
|
-
let {
|
|
31137
|
-
graph,
|
|
31138
|
-
range,
|
|
31139
|
-
step
|
|
31140
|
-
} = _ref4;
|
|
31141
|
-
let coords = graph.coords?.slice();
|
|
31142
|
-
if (coords) {
|
|
31143
|
-
return coords;
|
|
31144
|
-
}
|
|
31145
|
-
const n = graph.numSides || 3;
|
|
31146
|
-
if (n === "unlimited") {
|
|
31147
|
-
coords = [];
|
|
31148
|
-
} else {
|
|
31149
|
-
const angle = 2 * Math.PI / n;
|
|
31150
|
-
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
31151
|
-
|
|
31152
|
-
// TODO(alex): Generalize this to more than just triangles so that
|
|
31153
|
-
// all polygons have whole number side lengths if snapping to sides
|
|
31154
|
-
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
31155
|
-
|
|
31156
|
-
// Generate coords of a regular polygon with n sides
|
|
31157
|
-
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
31158
|
-
}
|
|
31159
|
-
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
31160
|
-
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
31161
|
-
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
31162
|
-
return coords;
|
|
31163
|
-
};
|
|
31164
|
-
|
|
31165
31313
|
const renderGraph = props => {
|
|
31166
31314
|
const {
|
|
31167
31315
|
state,
|
|
@@ -31198,20 +31346,46 @@ const renderGraph = props => {
|
|
|
31198
31346
|
dispatch: dispatch
|
|
31199
31347
|
});
|
|
31200
31348
|
case "circle":
|
|
31201
|
-
|
|
31349
|
+
return /*#__PURE__*/React__namespace.createElement(CircleGraph, {
|
|
31350
|
+
graphState: state,
|
|
31351
|
+
dispatch: dispatch
|
|
31352
|
+
});
|
|
31202
31353
|
default:
|
|
31203
31354
|
return new wonderStuffCore.UnreachableCaseError(type);
|
|
31204
31355
|
}
|
|
31205
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
|
+
}
|
|
31206
31376
|
const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
|
|
31207
31377
|
const [state, dispatch] = React__namespace.useReducer(interactiveGraphReducer, props, initializeGraphState);
|
|
31208
31378
|
React.useImperativeHandle(ref, () => ({
|
|
31209
31379
|
getUserInput: () => getGradableGraph(state, props.graph)
|
|
31210
31380
|
}));
|
|
31211
|
-
|
|
31381
|
+
function onChange(next) {
|
|
31382
|
+
props.onChange(mafsStateToInteractiveGraph(next));
|
|
31383
|
+
}
|
|
31384
|
+
return /*#__PURE__*/React__namespace.createElement(MafsGraph, _extends({}, props, {
|
|
31212
31385
|
state: state,
|
|
31213
|
-
dispatch: dispatch
|
|
31214
|
-
|
|
31386
|
+
dispatch: dispatch,
|
|
31387
|
+
onChange: onChange
|
|
31388
|
+
}));
|
|
31215
31389
|
});
|
|
31216
31390
|
const MafsGraph = props => {
|
|
31217
31391
|
const {
|
|
@@ -43385,7 +43559,7 @@ const init = function (options) {
|
|
|
43385
43559
|
|
|
43386
43560
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
43387
43561
|
const libName = "@khanacademy/perseus";
|
|
43388
|
-
const libVersion = "22.
|
|
43562
|
+
const libVersion = "22.4.0";
|
|
43389
43563
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
43390
43564
|
|
|
43391
43565
|
const apiVersion = {
|