@khanacademy/perseus 23.3.0 → 23.5.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 +8 -0
- package/dist/es/index.js +469 -311
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +8 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +482 -328
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +1 -1
- package/dist/perseus-types.d.ts +17 -9
- package/dist/server-item-renderer.d.ts +1 -1
- package/dist/widgets/expression.d.ts +6 -6
- package/dist/widgets/interactive-graphs/graphs/components/angle.d.ts +2 -1
- package/dist/widgets/interactive-graphs/graphs/components/movable-point.d.ts +1 -0
- package/dist/widgets/interactive-graphs/graphs/quadratic.d.ts +1 -2
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +10 -3
- package/dist/widgets/interactive-graphs/locked-polygon.d.ts +4 -0
- package/dist/widgets/interactive-graphs/mafs-graph.d.ts +2 -1
- package/dist/widgets/interactive-graphs/reducer/initialize-graph-state.d.ts +10 -0
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-action.d.ts +8 -1
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-state.d.ts +2 -9
- package/dist/widgets/interactive-graphs/types.d.ts +1 -0
- package/dist/widgets/label-image.d.ts +2 -2
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -3304,7 +3304,7 @@ const DeprecationMixin$1 = {
|
|
|
3304
3304
|
/**
|
|
3305
3305
|
* Approximate equality on numbers and primitives.
|
|
3306
3306
|
*/
|
|
3307
|
-
function eq$
|
|
3307
|
+
function eq$3(x, y) {
|
|
3308
3308
|
if (typeof x === "number" && typeof y === "number") {
|
|
3309
3309
|
return Math.abs(x - y) < 1e-9;
|
|
3310
3310
|
}
|
|
@@ -3331,7 +3331,7 @@ function deepEq$5(x, y) {
|
|
|
3331
3331
|
return false;
|
|
3332
3332
|
}
|
|
3333
3333
|
if (typeof x === "function" && typeof y === "function") {
|
|
3334
|
-
return eq$
|
|
3334
|
+
return eq$3(x, y);
|
|
3335
3335
|
}
|
|
3336
3336
|
if (typeof x === "function" || typeof y === "function") {
|
|
3337
3337
|
return false;
|
|
@@ -3348,7 +3348,7 @@ function deepEq$5(x, y) {
|
|
|
3348
3348
|
if (typeof x === "object" && !!x || typeof y === "object" && !!y) {
|
|
3349
3349
|
return false;
|
|
3350
3350
|
}
|
|
3351
|
-
return eq$
|
|
3351
|
+
return eq$3(x, y);
|
|
3352
3352
|
}
|
|
3353
3353
|
|
|
3354
3354
|
/**
|
|
@@ -3647,7 +3647,7 @@ const Util = {
|
|
|
3647
3647
|
tickStepFromNumTicks,
|
|
3648
3648
|
constrainedTickStepsFromTickSteps,
|
|
3649
3649
|
DeprecationMixin: DeprecationMixin$1,
|
|
3650
|
-
eq: eq$
|
|
3650
|
+
eq: eq$3,
|
|
3651
3651
|
deepEq: deepEq$5,
|
|
3652
3652
|
parseQueryString,
|
|
3653
3653
|
updateQueryString: updateQueryString$2,
|
|
@@ -10885,7 +10885,7 @@ class WrappedLine extends WrappedDrawing {
|
|
|
10885
10885
|
* A collection of geomtry-related utility functions
|
|
10886
10886
|
*/
|
|
10887
10887
|
const {
|
|
10888
|
-
eq: eq$
|
|
10888
|
+
eq: eq$2,
|
|
10889
10889
|
deepEq: deepEq$4
|
|
10890
10890
|
} = Util;
|
|
10891
10891
|
|
|
@@ -10893,17 +10893,21 @@ const {
|
|
|
10893
10893
|
|
|
10894
10894
|
// a, b, c
|
|
10895
10895
|
|
|
10896
|
+
// Given a number, return whether it is positive (1), negative (-1), or zero (0)
|
|
10896
10897
|
function sign(val) {
|
|
10897
|
-
if (eq$
|
|
10898
|
+
if (eq$2(val, 0)) {
|
|
10898
10899
|
return 0;
|
|
10899
10900
|
}
|
|
10900
10901
|
return val > 0 ? 1 : -1;
|
|
10901
10902
|
}
|
|
10903
|
+
|
|
10904
|
+
// Determine whether three points are collinear (0), for a clockwise turn (negative),
|
|
10905
|
+
// or counterclockwise turn (positive)
|
|
10902
10906
|
function ccw(a, b, c) {
|
|
10903
10907
|
return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
|
|
10904
10908
|
}
|
|
10905
10909
|
function collinear(a, b, c) {
|
|
10906
|
-
return eq$
|
|
10910
|
+
return eq$2(ccw(a, b, c), 0);
|
|
10907
10911
|
}
|
|
10908
10912
|
|
|
10909
10913
|
// Given rect bounding points A and B, whether point C is inside the rect
|
|
@@ -11048,7 +11052,7 @@ function similar(coords1, coords2, tolerance) {
|
|
|
11048
11052
|
return pair[0] / pair[1];
|
|
11049
11053
|
});
|
|
11050
11054
|
const same = ___default["default"].all(factors, function (factor) {
|
|
11051
|
-
return eq$
|
|
11055
|
+
return eq$2(factors[0], factor);
|
|
11052
11056
|
});
|
|
11053
11057
|
const congruentEnough = ___default["default"].all(sidePairs, function (pair) {
|
|
11054
11058
|
return kmath.number.equal(pair[0], pair[1], tolerance);
|
|
@@ -11101,7 +11105,7 @@ function rotate(array, n) {
|
|
|
11101
11105
|
return array.slice(n).concat(array.slice(0, n));
|
|
11102
11106
|
}
|
|
11103
11107
|
function getLineEquation(first, second) {
|
|
11104
|
-
if (eq$
|
|
11108
|
+
if (eq$2(first[0], second[0])) {
|
|
11105
11109
|
return "x = " + first[0].toFixed(3);
|
|
11106
11110
|
}
|
|
11107
11111
|
const m = (second[1] - first[1]) / (second[0] - first[0]);
|
|
@@ -28943,13 +28947,15 @@ const ItemExtras = [
|
|
|
28943
28947
|
|
|
28944
28948
|
const PerseusExpressionAnswerFormConsidered = ["correct", "wrong", "ungraded"];
|
|
28945
28949
|
const lockedFigureColors = {
|
|
28950
|
+
blue: "#3D7586",
|
|
28946
28951
|
green: "#447A53",
|
|
28947
28952
|
grayH: "#3B3D45",
|
|
28948
28953
|
purple: "#594094",
|
|
28949
28954
|
pink: "#B25071",
|
|
28950
|
-
red: "#D92916"
|
|
28955
|
+
red: "#D92916",
|
|
28956
|
+
orange: "#946700"
|
|
28951
28957
|
};
|
|
28952
|
-
const
|
|
28958
|
+
const lockedFigureFillStyles = {
|
|
28953
28959
|
none: 0,
|
|
28954
28960
|
solid: 1,
|
|
28955
28961
|
translucent: 0.4
|
|
@@ -28969,7 +28975,7 @@ const LockedEllipse = props => {
|
|
|
28969
28975
|
center: center,
|
|
28970
28976
|
radius: radius,
|
|
28971
28977
|
angle: angle,
|
|
28972
|
-
fillOpacity:
|
|
28978
|
+
fillOpacity: lockedFigureFillStyles[fillStyle],
|
|
28973
28979
|
strokeStyle: strokeStyle,
|
|
28974
28980
|
color: lockedFigureColors[color]
|
|
28975
28981
|
});
|
|
@@ -29228,6 +29234,29 @@ const LockedPoint = props => {
|
|
|
29228
29234
|
});
|
|
29229
29235
|
};
|
|
29230
29236
|
|
|
29237
|
+
const LockedPolygon = props => {
|
|
29238
|
+
const {
|
|
29239
|
+
points,
|
|
29240
|
+
color,
|
|
29241
|
+
showVertices,
|
|
29242
|
+
fillStyle,
|
|
29243
|
+
strokeStyle
|
|
29244
|
+
} = props;
|
|
29245
|
+
return /*#__PURE__*/React__namespace.createElement("g", {
|
|
29246
|
+
className: "locked-polygon"
|
|
29247
|
+
}, /*#__PURE__*/React__namespace.createElement(mafs.Polygon, {
|
|
29248
|
+
points: [...points],
|
|
29249
|
+
fillOpacity: lockedFigureFillStyles[fillStyle],
|
|
29250
|
+
strokeStyle: strokeStyle,
|
|
29251
|
+
color: lockedFigureColors[color]
|
|
29252
|
+
}), showVertices && points.map((point, index) => /*#__PURE__*/React__namespace.createElement(mafs.Point, {
|
|
29253
|
+
key: `locked-polygon-point-${index}`,
|
|
29254
|
+
x: point[0],
|
|
29255
|
+
y: point[1],
|
|
29256
|
+
color: lockedFigureColors[color]
|
|
29257
|
+
})));
|
|
29258
|
+
};
|
|
29259
|
+
|
|
29231
29260
|
const LockedVector = props => {
|
|
29232
29261
|
const {
|
|
29233
29262
|
color,
|
|
@@ -29258,13 +29287,17 @@ const GraphLockedLayer = props => {
|
|
|
29258
29287
|
key: `line-${index}`,
|
|
29259
29288
|
range: props.range
|
|
29260
29289
|
}, figure));
|
|
29290
|
+
case "vector":
|
|
29291
|
+
return /*#__PURE__*/React__namespace.createElement(LockedVector, _extends({
|
|
29292
|
+
key: `vector-${index}`
|
|
29293
|
+
}, figure));
|
|
29261
29294
|
case "ellipse":
|
|
29262
29295
|
return /*#__PURE__*/React__namespace.createElement(LockedEllipse, _extends({
|
|
29263
29296
|
key: `ellipse-${index}`
|
|
29264
29297
|
}, figure));
|
|
29265
|
-
case "
|
|
29266
|
-
return /*#__PURE__*/React__namespace.createElement(
|
|
29267
|
-
key: `
|
|
29298
|
+
case "polygon":
|
|
29299
|
+
return /*#__PURE__*/React__namespace.createElement(LockedPolygon, _extends({
|
|
29300
|
+
key: `polygon-${index}`
|
|
29268
29301
|
}, figure));
|
|
29269
29302
|
default:
|
|
29270
29303
|
/**
|
|
@@ -29278,6 +29311,13 @@ const GraphLockedLayer = props => {
|
|
|
29278
29311
|
}));
|
|
29279
29312
|
};
|
|
29280
29313
|
|
|
29314
|
+
const REINITIALIZE = "reinitialize";
|
|
29315
|
+
function reinitialize(params) {
|
|
29316
|
+
return {
|
|
29317
|
+
type: REINITIALIZE,
|
|
29318
|
+
params
|
|
29319
|
+
};
|
|
29320
|
+
}
|
|
29281
29321
|
const MOVE_CONTROL_POINT = "move-control-point";
|
|
29282
29322
|
function moveControlPoint(pointIndex, destination, itemIndex) {
|
|
29283
29323
|
return {
|
|
@@ -29542,6 +29582,7 @@ function useControlPoint(point, color, onMovePoint) {
|
|
|
29542
29582
|
});
|
|
29543
29583
|
const focusableHandle = /*#__PURE__*/React__namespace.createElement("g", {
|
|
29544
29584
|
"data-testid": "movable-point__focusable-handle",
|
|
29585
|
+
className: "movable-point__focusable-handle",
|
|
29545
29586
|
tabIndex: 0,
|
|
29546
29587
|
ref: keyboardHandleRef,
|
|
29547
29588
|
onFocus: () => setFocused(true),
|
|
@@ -29711,7 +29752,6 @@ const LinearSystemGraph = props => {
|
|
|
29711
29752
|
const {
|
|
29712
29753
|
coords: lines
|
|
29713
29754
|
} = props.graphState;
|
|
29714
|
-
const colors = ["var(--movable-line-stroke-color)", "var(--mafs-violet)"];
|
|
29715
29755
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, lines?.map((line, i) => /*#__PURE__*/React__namespace.createElement(MovableLine, {
|
|
29716
29756
|
key: i,
|
|
29717
29757
|
points: line,
|
|
@@ -29723,7 +29763,7 @@ const LinearSystemGraph = props => {
|
|
|
29723
29763
|
end: true
|
|
29724
29764
|
},
|
|
29725
29765
|
onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i)),
|
|
29726
|
-
color:
|
|
29766
|
+
color: "var(--movable-line-stroke-color)"
|
|
29727
29767
|
})), ";");
|
|
29728
29768
|
};
|
|
29729
29769
|
|
|
@@ -29798,7 +29838,8 @@ const Angle = _ref => {
|
|
|
29798
29838
|
endPoints,
|
|
29799
29839
|
range,
|
|
29800
29840
|
polygonLines,
|
|
29801
|
-
showAngles
|
|
29841
|
+
showAngles,
|
|
29842
|
+
snapTo
|
|
29802
29843
|
} = _ref;
|
|
29803
29844
|
const [centerX, centerY] = centerPoint;
|
|
29804
29845
|
const areClockwise = clockwise$1([centerPoint, ...endPoints]);
|
|
@@ -29836,7 +29877,9 @@ const Angle = _ref => {
|
|
|
29836
29877
|
if (isOutside) {
|
|
29837
29878
|
angleInDegrees = 360 - angleInDegrees;
|
|
29838
29879
|
}
|
|
29839
|
-
|
|
29880
|
+
|
|
29881
|
+
// Only want to show whole numbers when in angles mode
|
|
29882
|
+
const angleLabelNumber = parseFloat(angleInDegrees.toFixed(snapTo === "angles" ? 0 : 1));
|
|
29840
29883
|
const angleLabel = Number.isInteger(angleLabelNumber) ? angleLabelNumber : "≈ " + angleLabelNumber;
|
|
29841
29884
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("filter", {
|
|
29842
29885
|
id: "background",
|
|
@@ -29927,15 +29970,17 @@ const StyledMovablePoint = props => {
|
|
|
29927
29970
|
point,
|
|
29928
29971
|
onMove,
|
|
29929
29972
|
cursor,
|
|
29930
|
-
color = wonderBlocksTokens.color.blue
|
|
29973
|
+
color = wonderBlocksTokens.color.blue,
|
|
29974
|
+
snapTo
|
|
29931
29975
|
} = props;
|
|
29976
|
+
const snapToValue = snapTo ?? "grid";
|
|
29932
29977
|
const {
|
|
29933
29978
|
dragging
|
|
29934
29979
|
} = mafs.useMovable({
|
|
29935
29980
|
gestureTarget: elementRef,
|
|
29936
29981
|
point,
|
|
29937
29982
|
onMove,
|
|
29938
|
-
constrain: p => snap(snapStep, p)
|
|
29983
|
+
constrain: p => ["angles", "sides"].includes(snapToValue) ? p : snap(snapStep, p)
|
|
29939
29984
|
});
|
|
29940
29985
|
return /*#__PURE__*/React__namespace.createElement(MovablePointView, {
|
|
29941
29986
|
ref: elementRef,
|
|
@@ -29961,7 +30006,8 @@ const PolygonGraph = props => {
|
|
|
29961
30006
|
showAngles,
|
|
29962
30007
|
showSides,
|
|
29963
30008
|
range,
|
|
29964
|
-
snapStep
|
|
30009
|
+
snapStep,
|
|
30010
|
+
snapTo
|
|
29965
30011
|
} = props.graphState;
|
|
29966
30012
|
|
|
29967
30013
|
// TODO(benchristel): can the default set of points be removed here? I don't
|
|
@@ -29969,6 +30015,7 @@ const PolygonGraph = props => {
|
|
|
29969
30015
|
const points = coords ?? [[0, 0]];
|
|
29970
30016
|
const ref = React__namespace.useRef(null);
|
|
29971
30017
|
const dragReferencePoint = points[0];
|
|
30018
|
+
const snapToValue = snapTo ?? "grid";
|
|
29972
30019
|
const {
|
|
29973
30020
|
dragging
|
|
29974
30021
|
} = mafs.useMovable({
|
|
@@ -29978,7 +30025,7 @@ const PolygonGraph = props => {
|
|
|
29978
30025
|
const delta = mafs.vec.sub(newPoint, dragReferencePoint);
|
|
29979
30026
|
dispatch(moveAll(delta));
|
|
29980
30027
|
},
|
|
29981
|
-
constrain: p => snap(snapStep, p)
|
|
30028
|
+
constrain: p => ["angles", "sides"].includes(snapToValue) ? p : snap(snapStep, p)
|
|
29982
30029
|
});
|
|
29983
30030
|
const active = hovered || focused || dragging;
|
|
29984
30031
|
const lines = getLines(points);
|
|
@@ -30001,12 +30048,13 @@ const PolygonGraph = props => {
|
|
|
30001
30048
|
active: active,
|
|
30002
30049
|
range: range,
|
|
30003
30050
|
polygonLines: lines,
|
|
30004
|
-
showAngles: !!showAngles
|
|
30051
|
+
showAngles: !!showAngles,
|
|
30052
|
+
snapTo: snapToValue
|
|
30005
30053
|
});
|
|
30006
30054
|
}), showSides && lines.map((_ref, i) => {
|
|
30007
30055
|
let [start, end] = _ref;
|
|
30008
30056
|
const [x, y] = mafs.vec.midpoint(start, end);
|
|
30009
|
-
const length = parseFloat(mafs.vec.dist(start, end).toFixed(1));
|
|
30057
|
+
const length = parseFloat(mafs.vec.dist(start, end).toFixed(snapTo === "sides" ? 0 : 1));
|
|
30010
30058
|
return /*#__PURE__*/React__namespace.createElement(TextLabel, {
|
|
30011
30059
|
key: "side-" + i,
|
|
30012
30060
|
x: x,
|
|
@@ -30030,153 +30078,12 @@ const PolygonGraph = props => {
|
|
|
30030
30078
|
}
|
|
30031
30079
|
}), points.map((point, i) => /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
|
|
30032
30080
|
key: "point-" + i,
|
|
30081
|
+
snapTo: snapTo,
|
|
30033
30082
|
point: point,
|
|
30034
30083
|
onMove: destination => dispatch(movePoint(i, destination))
|
|
30035
30084
|
})));
|
|
30036
30085
|
};
|
|
30037
30086
|
|
|
30038
|
-
function initializeGraphState(params) {
|
|
30039
|
-
const {
|
|
30040
|
-
graph,
|
|
30041
|
-
step,
|
|
30042
|
-
snapStep,
|
|
30043
|
-
range
|
|
30044
|
-
} = params;
|
|
30045
|
-
const shared = {
|
|
30046
|
-
hasBeenInteractedWith: false,
|
|
30047
|
-
range,
|
|
30048
|
-
snapStep
|
|
30049
|
-
};
|
|
30050
|
-
switch (graph.type) {
|
|
30051
|
-
case "segment":
|
|
30052
|
-
return {
|
|
30053
|
-
...shared,
|
|
30054
|
-
type: "segment",
|
|
30055
|
-
coords: getSegmentCoords({
|
|
30056
|
-
graph,
|
|
30057
|
-
step,
|
|
30058
|
-
range
|
|
30059
|
-
})
|
|
30060
|
-
};
|
|
30061
|
-
case "linear":
|
|
30062
|
-
return {
|
|
30063
|
-
...shared,
|
|
30064
|
-
type: graph.type,
|
|
30065
|
-
coords: getLineCoords({
|
|
30066
|
-
graph,
|
|
30067
|
-
range,
|
|
30068
|
-
step
|
|
30069
|
-
})
|
|
30070
|
-
};
|
|
30071
|
-
case "ray":
|
|
30072
|
-
return {
|
|
30073
|
-
...shared,
|
|
30074
|
-
type: graph.type,
|
|
30075
|
-
coords: getLineCoords({
|
|
30076
|
-
graph,
|
|
30077
|
-
range,
|
|
30078
|
-
step
|
|
30079
|
-
})
|
|
30080
|
-
};
|
|
30081
|
-
case "linear-system":
|
|
30082
|
-
return {
|
|
30083
|
-
...shared,
|
|
30084
|
-
type: graph.type,
|
|
30085
|
-
coords: getLinearSystemCoords({
|
|
30086
|
-
graph,
|
|
30087
|
-
range,
|
|
30088
|
-
step
|
|
30089
|
-
})
|
|
30090
|
-
};
|
|
30091
|
-
case "polygon":
|
|
30092
|
-
return {
|
|
30093
|
-
...shared,
|
|
30094
|
-
type: "polygon",
|
|
30095
|
-
showAngles: Boolean(graph.showAngles),
|
|
30096
|
-
showSides: Boolean(graph.showSides),
|
|
30097
|
-
coords: getPolygonCoords({
|
|
30098
|
-
graph,
|
|
30099
|
-
range,
|
|
30100
|
-
step
|
|
30101
|
-
})
|
|
30102
|
-
};
|
|
30103
|
-
case "point":
|
|
30104
|
-
return {
|
|
30105
|
-
...shared,
|
|
30106
|
-
type: graph.type,
|
|
30107
|
-
coords: getDefaultPoints({
|
|
30108
|
-
graph,
|
|
30109
|
-
step,
|
|
30110
|
-
range
|
|
30111
|
-
})
|
|
30112
|
-
};
|
|
30113
|
-
case "circle":
|
|
30114
|
-
return {
|
|
30115
|
-
...shared,
|
|
30116
|
-
type: graph.type,
|
|
30117
|
-
...getCircleCoords(graph)
|
|
30118
|
-
};
|
|
30119
|
-
case "quadratic":
|
|
30120
|
-
return {
|
|
30121
|
-
...shared,
|
|
30122
|
-
type: graph.type,
|
|
30123
|
-
coords: getQuadraticCoords(graph, range, step)
|
|
30124
|
-
};
|
|
30125
|
-
case "sinusoid":
|
|
30126
|
-
return {
|
|
30127
|
-
...shared,
|
|
30128
|
-
type: graph.type,
|
|
30129
|
-
coords: getSinusoidCoords(graph, range, step)
|
|
30130
|
-
};
|
|
30131
|
-
case "angle":
|
|
30132
|
-
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
30133
|
-
default:
|
|
30134
|
-
throw new wonderStuffCore.UnreachableCaseError(graph);
|
|
30135
|
-
}
|
|
30136
|
-
}
|
|
30137
|
-
const getDefaultPoints = _ref => {
|
|
30138
|
-
let {
|
|
30139
|
-
graph,
|
|
30140
|
-
range,
|
|
30141
|
-
step
|
|
30142
|
-
} = _ref;
|
|
30143
|
-
const numPoints = graph.numPoints || 1;
|
|
30144
|
-
let coords = graph.coords?.slice();
|
|
30145
|
-
if (coords) {
|
|
30146
|
-
return coords;
|
|
30147
|
-
}
|
|
30148
|
-
switch (numPoints) {
|
|
30149
|
-
case 1:
|
|
30150
|
-
// Back in the day, one point's coords were in graph.coord
|
|
30151
|
-
coords = [graph.coord || [0, 0]];
|
|
30152
|
-
break;
|
|
30153
|
-
case 2:
|
|
30154
|
-
coords = [[-5, 0], [5, 0]];
|
|
30155
|
-
break;
|
|
30156
|
-
case 3:
|
|
30157
|
-
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
30158
|
-
break;
|
|
30159
|
-
case 4:
|
|
30160
|
-
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
30161
|
-
break;
|
|
30162
|
-
case 5:
|
|
30163
|
-
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
30164
|
-
break;
|
|
30165
|
-
case 6:
|
|
30166
|
-
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
30167
|
-
break;
|
|
30168
|
-
default:
|
|
30169
|
-
coords = [];
|
|
30170
|
-
break;
|
|
30171
|
-
}
|
|
30172
|
-
// Transform coords from their -10 to 10 space to 0 to 1
|
|
30173
|
-
// because of the old graph.coord, and also it's easier.
|
|
30174
|
-
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
30175
|
-
return normalizePoints(range, step, newCoords);
|
|
30176
|
-
};
|
|
30177
|
-
|
|
30178
|
-
// TS v4 doesn't narrow return types, while v5 does.
|
|
30179
|
-
// Instead of updating to v5, using generic type to relate input and output types.
|
|
30180
30087
|
function getGradableGraph(state, initialGraph) {
|
|
30181
30088
|
if (!state.hasBeenInteractedWith) {
|
|
30182
30089
|
return {
|
|
@@ -30240,118 +30147,6 @@ function getGradableGraph(state, initialGraph) {
|
|
|
30240
30147
|
}
|
|
30241
30148
|
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
30242
30149
|
}
|
|
30243
|
-
const getSegmentCoords = _ref2 => {
|
|
30244
|
-
let {
|
|
30245
|
-
graph,
|
|
30246
|
-
range,
|
|
30247
|
-
step
|
|
30248
|
-
} = _ref2;
|
|
30249
|
-
if (graph.coords) {
|
|
30250
|
-
return graph.coords;
|
|
30251
|
-
}
|
|
30252
|
-
const ys = n => {
|
|
30253
|
-
switch (n) {
|
|
30254
|
-
case 2:
|
|
30255
|
-
return [5, -5];
|
|
30256
|
-
case 3:
|
|
30257
|
-
return [5, 0, -5];
|
|
30258
|
-
case 4:
|
|
30259
|
-
return [6, 2, -2, -6];
|
|
30260
|
-
case 5:
|
|
30261
|
-
return [6, 3, 0, -3, -6];
|
|
30262
|
-
case 6:
|
|
30263
|
-
return [5, 3, 1, -1, -3, -5];
|
|
30264
|
-
default:
|
|
30265
|
-
return [5];
|
|
30266
|
-
}
|
|
30267
|
-
};
|
|
30268
|
-
const defaultRange = [[-10, 10], [-10, 10]];
|
|
30269
|
-
return ys(graph.numSegments).map(y => {
|
|
30270
|
-
let endpoints = [[-5, y], [5, y]];
|
|
30271
|
-
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
30272
|
-
endpoints = normalizePoints(range, step, endpoints);
|
|
30273
|
-
return endpoints;
|
|
30274
|
-
});
|
|
30275
|
-
};
|
|
30276
|
-
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
30277
|
-
const getLineCoords = _ref3 => {
|
|
30278
|
-
let {
|
|
30279
|
-
graph,
|
|
30280
|
-
range,
|
|
30281
|
-
step
|
|
30282
|
-
} = _ref3;
|
|
30283
|
-
if (graph.coords) {
|
|
30284
|
-
return graph.coords;
|
|
30285
|
-
}
|
|
30286
|
-
return normalizePoints(range, step, defaultLinearCoords[0]);
|
|
30287
|
-
};
|
|
30288
|
-
const getLinearSystemCoords = _ref4 => {
|
|
30289
|
-
let {
|
|
30290
|
-
graph,
|
|
30291
|
-
range,
|
|
30292
|
-
step
|
|
30293
|
-
} = _ref4;
|
|
30294
|
-
if (graph.coords) {
|
|
30295
|
-
return graph.coords;
|
|
30296
|
-
}
|
|
30297
|
-
return defaultLinearCoords.map(points => normalizePoints(range, step, points));
|
|
30298
|
-
};
|
|
30299
|
-
const getPolygonCoords = _ref5 => {
|
|
30300
|
-
let {
|
|
30301
|
-
graph,
|
|
30302
|
-
range,
|
|
30303
|
-
step
|
|
30304
|
-
} = _ref5;
|
|
30305
|
-
let coords = graph.coords?.slice();
|
|
30306
|
-
if (coords) {
|
|
30307
|
-
return coords;
|
|
30308
|
-
}
|
|
30309
|
-
const n = graph.numSides || 3;
|
|
30310
|
-
if (n === "unlimited") {
|
|
30311
|
-
coords = [];
|
|
30312
|
-
} else {
|
|
30313
|
-
const angle = 2 * Math.PI / n;
|
|
30314
|
-
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
30315
|
-
|
|
30316
|
-
// TODO(alex): Generalize this to more than just triangles so that
|
|
30317
|
-
// all polygons have whole number side lengths if snapping to sides
|
|
30318
|
-
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
30319
|
-
|
|
30320
|
-
// Generate coords of a regular polygon with n sides
|
|
30321
|
-
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
30322
|
-
}
|
|
30323
|
-
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
30324
|
-
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
30325
|
-
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
30326
|
-
return coords;
|
|
30327
|
-
};
|
|
30328
|
-
const getSinusoidCoords = (graph, range, step) => {
|
|
30329
|
-
if (graph.coords) {
|
|
30330
|
-
return [graph.coords[0], graph.coords[1]];
|
|
30331
|
-
}
|
|
30332
|
-
let coords = [[0.5, 0.5], [0.65, 0.6]];
|
|
30333
|
-
coords = normalizePoints(range, step, coords, true);
|
|
30334
|
-
return coords;
|
|
30335
|
-
};
|
|
30336
|
-
const getQuadraticCoords = (graph, range, step) => {
|
|
30337
|
-
if (graph.coords) {
|
|
30338
|
-
return graph.coords;
|
|
30339
|
-
}
|
|
30340
|
-
const defaultCoords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
|
|
30341
|
-
return normalizePoints(range, step, defaultCoords, true);
|
|
30342
|
-
};
|
|
30343
|
-
const getCircleCoords = (graph, range, step) => {
|
|
30344
|
-
if (graph.center != null && graph.radius != null) {
|
|
30345
|
-
return {
|
|
30346
|
-
center: graph.center,
|
|
30347
|
-
radiusPoint: mafs.vec.add(graph.center, [graph.radius, 0])
|
|
30348
|
-
};
|
|
30349
|
-
}
|
|
30350
|
-
return {
|
|
30351
|
-
center: [0, 0],
|
|
30352
|
-
radiusPoint: [1, 0]
|
|
30353
|
-
};
|
|
30354
|
-
};
|
|
30355
30150
|
|
|
30356
30151
|
/**
|
|
30357
30152
|
* determine radius of a circle graph
|
|
@@ -30485,38 +30280,16 @@ function QuadraticGraph(props) {
|
|
|
30485
30280
|
|
|
30486
30281
|
// Calculate the y value based on the current x value
|
|
30487
30282
|
const y = x => (a * x + b) * x + c;
|
|
30488
|
-
const handleOnMove = (destination, elementId) => {
|
|
30489
|
-
// If the destination is invalid, we want do not want to move the point
|
|
30490
|
-
const validDestination = isValidDestination(destination, elementId, coords);
|
|
30491
|
-
if (validDestination === false) {
|
|
30492
|
-
return;
|
|
30493
|
-
}
|
|
30494
|
-
dispatch(movePoint(elementId, destination));
|
|
30495
|
-
};
|
|
30496
30283
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Plot.OfX, {
|
|
30497
30284
|
y: y,
|
|
30498
30285
|
color: wonderBlocksTokens.color.blue
|
|
30499
30286
|
}), coords.map((coord, i) => /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
|
|
30500
30287
|
key: "point-" + i,
|
|
30501
30288
|
point: coord,
|
|
30502
|
-
onMove: destination =>
|
|
30289
|
+
onMove: destination => dispatch(movePoint(i, destination))
|
|
30503
30290
|
})));
|
|
30504
30291
|
}
|
|
30505
30292
|
|
|
30506
|
-
// Ensure that we are only snapping to coordinates that result in a valid quadratic equation
|
|
30507
|
-
const isValidDestination = (destination, elementId, coords) => {
|
|
30508
|
-
// Set up the new coords and check if the quadratic coefficients are valid
|
|
30509
|
-
const newCoords = [...coords];
|
|
30510
|
-
newCoords[elementId] = destination;
|
|
30511
|
-
const QuadraticCoefficients = getQuadraticCoefficients(newCoords);
|
|
30512
|
-
|
|
30513
|
-
// If the new destination results in an invalid quadratic equation, we don't want to move the point
|
|
30514
|
-
if (QuadraticCoefficients === undefined) {
|
|
30515
|
-
return false;
|
|
30516
|
-
}
|
|
30517
|
-
return true;
|
|
30518
|
-
};
|
|
30519
|
-
|
|
30520
30293
|
// Get the quadratic coefficients from the 3 control points
|
|
30521
30294
|
// These equations were originally set up in 2013 and may require some
|
|
30522
30295
|
// additional comments to help clarify the quadratic formula manipulations
|
|
@@ -31666,8 +31439,214 @@ const LegacyGrid = _ref => {
|
|
|
31666
31439
|
return null;
|
|
31667
31440
|
};
|
|
31668
31441
|
|
|
31442
|
+
function initializeGraphState(params) {
|
|
31443
|
+
const {
|
|
31444
|
+
graph,
|
|
31445
|
+
step,
|
|
31446
|
+
snapStep,
|
|
31447
|
+
range
|
|
31448
|
+
} = params;
|
|
31449
|
+
const shared = {
|
|
31450
|
+
hasBeenInteractedWith: false,
|
|
31451
|
+
range,
|
|
31452
|
+
snapStep
|
|
31453
|
+
};
|
|
31454
|
+
switch (graph.type) {
|
|
31455
|
+
case "segment":
|
|
31456
|
+
return {
|
|
31457
|
+
...shared,
|
|
31458
|
+
type: "segment",
|
|
31459
|
+
coords: getSegmentCoords(graph, range, step)
|
|
31460
|
+
};
|
|
31461
|
+
case "linear":
|
|
31462
|
+
return {
|
|
31463
|
+
...shared,
|
|
31464
|
+
type: graph.type,
|
|
31465
|
+
coords: getLineCoords(graph, range, step)
|
|
31466
|
+
};
|
|
31467
|
+
case "ray":
|
|
31468
|
+
return {
|
|
31469
|
+
...shared,
|
|
31470
|
+
type: graph.type,
|
|
31471
|
+
coords: getLineCoords(graph, range, step)
|
|
31472
|
+
};
|
|
31473
|
+
case "linear-system":
|
|
31474
|
+
return {
|
|
31475
|
+
...shared,
|
|
31476
|
+
type: graph.type,
|
|
31477
|
+
coords: getLinearSystemCoords(graph, range, step)
|
|
31478
|
+
};
|
|
31479
|
+
case "polygon":
|
|
31480
|
+
return {
|
|
31481
|
+
...shared,
|
|
31482
|
+
type: "polygon",
|
|
31483
|
+
showAngles: Boolean(graph.showAngles),
|
|
31484
|
+
showSides: Boolean(graph.showSides),
|
|
31485
|
+
coords: getPolygonCoords(graph, range, step),
|
|
31486
|
+
snapTo: graph.snapTo ?? "grid"
|
|
31487
|
+
};
|
|
31488
|
+
case "point":
|
|
31489
|
+
return {
|
|
31490
|
+
...shared,
|
|
31491
|
+
type: graph.type,
|
|
31492
|
+
coords: getPointCoords(graph, range, step)
|
|
31493
|
+
};
|
|
31494
|
+
case "circle":
|
|
31495
|
+
return {
|
|
31496
|
+
...shared,
|
|
31497
|
+
type: graph.type,
|
|
31498
|
+
...getCircleCoords(graph)
|
|
31499
|
+
};
|
|
31500
|
+
case "quadratic":
|
|
31501
|
+
return {
|
|
31502
|
+
...shared,
|
|
31503
|
+
type: graph.type,
|
|
31504
|
+
coords: getQuadraticCoords(graph, range, step)
|
|
31505
|
+
};
|
|
31506
|
+
case "sinusoid":
|
|
31507
|
+
return {
|
|
31508
|
+
...shared,
|
|
31509
|
+
type: graph.type,
|
|
31510
|
+
coords: getSinusoidCoords(graph, range, step)
|
|
31511
|
+
};
|
|
31512
|
+
case "angle":
|
|
31513
|
+
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
31514
|
+
default:
|
|
31515
|
+
throw new wonderStuffCore.UnreachableCaseError(graph);
|
|
31516
|
+
}
|
|
31517
|
+
}
|
|
31518
|
+
function getPointCoords(graph, range, step) {
|
|
31519
|
+
const numPoints = graph.numPoints || 1;
|
|
31520
|
+
let coords = graph.coords?.slice();
|
|
31521
|
+
if (coords) {
|
|
31522
|
+
return coords;
|
|
31523
|
+
}
|
|
31524
|
+
switch (numPoints) {
|
|
31525
|
+
case 1:
|
|
31526
|
+
// Back in the day, one point's coords were in graph.coord
|
|
31527
|
+
coords = [graph.coord || [0, 0]];
|
|
31528
|
+
break;
|
|
31529
|
+
case 2:
|
|
31530
|
+
coords = [[-5, 0], [5, 0]];
|
|
31531
|
+
break;
|
|
31532
|
+
case 3:
|
|
31533
|
+
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
31534
|
+
break;
|
|
31535
|
+
case 4:
|
|
31536
|
+
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
31537
|
+
break;
|
|
31538
|
+
case 5:
|
|
31539
|
+
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
31540
|
+
break;
|
|
31541
|
+
case 6:
|
|
31542
|
+
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
31543
|
+
break;
|
|
31544
|
+
default:
|
|
31545
|
+
coords = [];
|
|
31546
|
+
break;
|
|
31547
|
+
}
|
|
31548
|
+
// Transform coords from their -10 to 10 space to 0 to 1
|
|
31549
|
+
// because of the old graph.coord, and also it's easier.
|
|
31550
|
+
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
31551
|
+
return normalizePoints(range, step, newCoords);
|
|
31552
|
+
}
|
|
31553
|
+
function getSegmentCoords(graph, range, step) {
|
|
31554
|
+
if (graph.coords) {
|
|
31555
|
+
return graph.coords;
|
|
31556
|
+
}
|
|
31557
|
+
const ys = n => {
|
|
31558
|
+
switch (n) {
|
|
31559
|
+
case 2:
|
|
31560
|
+
return [5, -5];
|
|
31561
|
+
case 3:
|
|
31562
|
+
return [5, 0, -5];
|
|
31563
|
+
case 4:
|
|
31564
|
+
return [6, 2, -2, -6];
|
|
31565
|
+
case 5:
|
|
31566
|
+
return [6, 3, 0, -3, -6];
|
|
31567
|
+
case 6:
|
|
31568
|
+
return [5, 3, 1, -1, -3, -5];
|
|
31569
|
+
default:
|
|
31570
|
+
return [5];
|
|
31571
|
+
}
|
|
31572
|
+
};
|
|
31573
|
+
const defaultRange = [[-10, 10], [-10, 10]];
|
|
31574
|
+
return ys(graph.numSegments).map(y => {
|
|
31575
|
+
let endpoints = [[-5, y], [5, y]];
|
|
31576
|
+
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
31577
|
+
endpoints = normalizePoints(range, step, endpoints);
|
|
31578
|
+
return endpoints;
|
|
31579
|
+
});
|
|
31580
|
+
}
|
|
31581
|
+
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
31582
|
+
function getLineCoords(graph, range, step) {
|
|
31583
|
+
if (graph.coords) {
|
|
31584
|
+
return graph.coords;
|
|
31585
|
+
}
|
|
31586
|
+
return normalizePoints(range, step, defaultLinearCoords[0]);
|
|
31587
|
+
}
|
|
31588
|
+
function getLinearSystemCoords(graph, range, step) {
|
|
31589
|
+
if (graph.coords) {
|
|
31590
|
+
return graph.coords;
|
|
31591
|
+
}
|
|
31592
|
+
return defaultLinearCoords.map(points => normalizePoints(range, step, points));
|
|
31593
|
+
}
|
|
31594
|
+
function getPolygonCoords(graph, range, step) {
|
|
31595
|
+
let coords = graph.coords?.slice();
|
|
31596
|
+
if (coords) {
|
|
31597
|
+
return coords;
|
|
31598
|
+
}
|
|
31599
|
+
const n = graph.numSides || 3;
|
|
31600
|
+
if (n === "unlimited") {
|
|
31601
|
+
coords = [];
|
|
31602
|
+
} else {
|
|
31603
|
+
const angle = 2 * Math.PI / n;
|
|
31604
|
+
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
31605
|
+
|
|
31606
|
+
// TODO(alex): Generalize this to more than just triangles so that
|
|
31607
|
+
// all polygons have whole number side lengths if snapping to sides
|
|
31608
|
+
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
31609
|
+
|
|
31610
|
+
// Generate coords of a regular polygon with n sides
|
|
31611
|
+
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
31612
|
+
}
|
|
31613
|
+
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
31614
|
+
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
31615
|
+
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
31616
|
+
return coords;
|
|
31617
|
+
}
|
|
31618
|
+
function getSinusoidCoords(graph, range, step) {
|
|
31619
|
+
if (graph.coords) {
|
|
31620
|
+
return [graph.coords[0], graph.coords[1]];
|
|
31621
|
+
}
|
|
31622
|
+
let coords = [[0.5, 0.5], [0.65, 0.6]];
|
|
31623
|
+
coords = normalizePoints(range, step, coords, true);
|
|
31624
|
+
return coords;
|
|
31625
|
+
}
|
|
31626
|
+
function getQuadraticCoords(graph, range, step) {
|
|
31627
|
+
if (graph.coords) {
|
|
31628
|
+
return graph.coords;
|
|
31629
|
+
}
|
|
31630
|
+
const defaultCoords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
|
|
31631
|
+
return normalizePoints(range, step, defaultCoords, true);
|
|
31632
|
+
}
|
|
31633
|
+
function getCircleCoords(graph) {
|
|
31634
|
+
if (graph.center != null && graph.radius != null) {
|
|
31635
|
+
return {
|
|
31636
|
+
center: graph.center,
|
|
31637
|
+
radiusPoint: mafs.vec.add(graph.center, [graph.radius, 0])
|
|
31638
|
+
};
|
|
31639
|
+
}
|
|
31640
|
+
return {
|
|
31641
|
+
center: [0, 0],
|
|
31642
|
+
radiusPoint: [2, 0]
|
|
31643
|
+
};
|
|
31644
|
+
}
|
|
31645
|
+
|
|
31669
31646
|
function interactiveGraphReducer(state, action) {
|
|
31670
31647
|
switch (action.type) {
|
|
31648
|
+
case REINITIALIZE:
|
|
31649
|
+
return initializeGraphState(action.params);
|
|
31671
31650
|
case MOVE_CONTROL_POINT:
|
|
31672
31651
|
return doMoveControlPoint(state, action);
|
|
31673
31652
|
case MOVE_LINE:
|
|
@@ -31803,11 +31782,20 @@ function doMoveAll(state, action) {
|
|
|
31803
31782
|
switch (state.type) {
|
|
31804
31783
|
case "polygon":
|
|
31805
31784
|
{
|
|
31806
|
-
|
|
31807
|
-
|
|
31808
|
-
|
|
31809
|
-
|
|
31810
|
-
|
|
31785
|
+
let newCoords;
|
|
31786
|
+
if (state.snapTo === "sides" || state.snapTo === "angles") {
|
|
31787
|
+
const change = getChange(state.coords, action.delta, {
|
|
31788
|
+
snapStep: [0, 0],
|
|
31789
|
+
range
|
|
31790
|
+
});
|
|
31791
|
+
newCoords = state.coords.map(point => mafs.vec.add(point, change));
|
|
31792
|
+
} else {
|
|
31793
|
+
const change = getChange(state.coords, action.delta, {
|
|
31794
|
+
snapStep,
|
|
31795
|
+
range
|
|
31796
|
+
});
|
|
31797
|
+
newCoords = state.coords.map(point => snap(snapStep, mafs.vec.add(point, change)));
|
|
31798
|
+
}
|
|
31811
31799
|
return {
|
|
31812
31800
|
...state,
|
|
31813
31801
|
hasBeenInteractedWith: true,
|
|
@@ -31822,10 +31810,18 @@ function doMoveAll(state, action) {
|
|
|
31822
31810
|
function doMovePoint(state, action) {
|
|
31823
31811
|
switch (state.type) {
|
|
31824
31812
|
case "polygon":
|
|
31813
|
+
let newValue;
|
|
31814
|
+
if (state.snapTo === "sides") {
|
|
31815
|
+
newValue = boundAndSnapToSides(action.destination, state, action.index);
|
|
31816
|
+
} else if (state.snapTo === "angles") {
|
|
31817
|
+
newValue = boundAndSnapToAngle(action.destination, state, action.index);
|
|
31818
|
+
} else {
|
|
31819
|
+
newValue = boundAndSnapToGrid(action.destination, state);
|
|
31820
|
+
}
|
|
31825
31821
|
const newCoords = setAtIndex({
|
|
31826
31822
|
array: state.coords,
|
|
31827
31823
|
index: action.index,
|
|
31828
|
-
newValue:
|
|
31824
|
+
newValue: newValue
|
|
31829
31825
|
});
|
|
31830
31826
|
|
|
31831
31827
|
// Reject the move if it would cause the sides of the polygon to cross
|
|
@@ -31871,6 +31867,15 @@ function doMovePoint(state, action) {
|
|
|
31871
31867
|
}
|
|
31872
31868
|
case "quadratic":
|
|
31873
31869
|
{
|
|
31870
|
+
// Set up the new coords and check if the quadratic coefficients are valid
|
|
31871
|
+
const newCoords = [...state.coords];
|
|
31872
|
+
newCoords[action.index] = action.destination;
|
|
31873
|
+
const QuadraticCoefficients = getQuadraticCoefficients(newCoords);
|
|
31874
|
+
|
|
31875
|
+
// If the new destination results in an invalid quadratic equation, we don't want to move the point
|
|
31876
|
+
if (QuadraticCoefficients === undefined) {
|
|
31877
|
+
return state;
|
|
31878
|
+
}
|
|
31874
31879
|
return {
|
|
31875
31880
|
...state,
|
|
31876
31881
|
hasBeenInteractedWith: true,
|
|
@@ -31968,8 +31973,17 @@ function doChangeRange(state, action) {
|
|
|
31968
31973
|
range: action.range
|
|
31969
31974
|
};
|
|
31970
31975
|
}
|
|
31971
|
-
const
|
|
31976
|
+
const getDeltaVertex = (maxMoves, minMoves, delta) => {
|
|
31972
31977
|
const [deltaX, deltaY] = delta;
|
|
31978
|
+
const maxXMove = Math.min(...maxMoves.map(move => move[0]));
|
|
31979
|
+
const maxYMove = Math.min(...maxMoves.map(move => move[1]));
|
|
31980
|
+
const minXMove = Math.max(...minMoves.map(move => move[0]));
|
|
31981
|
+
const minYMove = Math.max(...minMoves.map(move => move[1]));
|
|
31982
|
+
const dx = clamp(deltaX, minXMove, maxXMove);
|
|
31983
|
+
const dy = clamp(deltaY, minYMove, maxYMove);
|
|
31984
|
+
return [dx, dy];
|
|
31985
|
+
};
|
|
31986
|
+
const getChange = (coords, delta, constraintOpts) => {
|
|
31973
31987
|
const maxMoves = coords.map(point => maxMove({
|
|
31974
31988
|
...constraintOpts,
|
|
31975
31989
|
point
|
|
@@ -31978,14 +31992,15 @@ const getChange = (coords, delta, constraintOpts) => {
|
|
|
31978
31992
|
...constraintOpts,
|
|
31979
31993
|
point
|
|
31980
31994
|
}));
|
|
31981
|
-
const
|
|
31982
|
-
const maxYMove = Math.min(...maxMoves.map(move => move[1]));
|
|
31983
|
-
const minXMove = Math.max(...minMoves.map(move => move[0]));
|
|
31984
|
-
const minYMove = Math.max(...minMoves.map(move => move[1]));
|
|
31985
|
-
const dx = clamp(deltaX, minXMove, maxXMove);
|
|
31986
|
-
const dy = clamp(deltaY, minYMove, maxYMove);
|
|
31995
|
+
const [dx, dy] = getDeltaVertex(maxMoves, minMoves, delta);
|
|
31987
31996
|
return [dx, dy];
|
|
31988
31997
|
};
|
|
31998
|
+
const eq$1 = Util.eq;
|
|
31999
|
+
|
|
32000
|
+
// Less than or approximately equal
|
|
32001
|
+
function leq$1(a, b) {
|
|
32002
|
+
return a < b || eq$1(a, b);
|
|
32003
|
+
}
|
|
31989
32004
|
function boundAndSnapToGrid(point, _ref) {
|
|
31990
32005
|
let {
|
|
31991
32006
|
snapStep,
|
|
@@ -31997,28 +32012,155 @@ function boundAndSnapToGrid(point, _ref) {
|
|
|
31997
32012
|
point
|
|
31998
32013
|
}));
|
|
31999
32014
|
}
|
|
32015
|
+
function boundAndSnapToAngle(destinationPoint, _ref2, index) {
|
|
32016
|
+
let {
|
|
32017
|
+
range,
|
|
32018
|
+
coords
|
|
32019
|
+
} = _ref2;
|
|
32020
|
+
const startingPoint = coords[index];
|
|
32021
|
+
|
|
32022
|
+
// Needed to prevent updating the original coords before the checks for
|
|
32023
|
+
// degenerate triangles and overlapping sides
|
|
32024
|
+
const coordsCopy = [...coords];
|
|
32025
|
+
|
|
32026
|
+
// Takes the destination point and makes sure it is within the bounds of the graph
|
|
32027
|
+
// SnapStep is [0, 0] because we don't want to snap to the grid
|
|
32028
|
+
coordsCopy[index] = bound$1({
|
|
32029
|
+
snapStep: [0, 0],
|
|
32030
|
+
range,
|
|
32031
|
+
point: destinationPoint
|
|
32032
|
+
});
|
|
32033
|
+
|
|
32034
|
+
// Gets the radian angles between the coords and maps them to degrees
|
|
32035
|
+
const angles = angleMeasures(coordsCopy).map(angle => angle * 180 / Math.PI);
|
|
32036
|
+
|
|
32037
|
+
// Gets the relative index of a point
|
|
32038
|
+
const rel = j => {
|
|
32039
|
+
return (index + j + coordsCopy.length) % coordsCopy.length;
|
|
32040
|
+
};
|
|
32041
|
+
|
|
32042
|
+
// Round the angles to left and right of the current point
|
|
32043
|
+
___default["default"].each([-1, 1], function (j) {
|
|
32044
|
+
angles[rel(j)] = Math.round(angles[rel(j)]);
|
|
32045
|
+
});
|
|
32046
|
+
const getAngle = function (a, vertex, b) {
|
|
32047
|
+
const angle = GraphUtils.findAngle(coordsCopy[rel(a)], coordsCopy[rel(b)], coordsCopy[rel(vertex)]);
|
|
32048
|
+
return (angle + 360) % 360;
|
|
32049
|
+
};
|
|
32050
|
+
const innerAngles = [angles[rel(-1)] - getAngle(-2, -1, 1), angles[rel(1)] - getAngle(-1, 1, 2)];
|
|
32051
|
+
innerAngles[2] = 180 - (innerAngles[0] + innerAngles[1]);
|
|
32052
|
+
|
|
32053
|
+
// Avoid degenerate triangles
|
|
32054
|
+
if (innerAngles.some(function (angle) {
|
|
32055
|
+
return leq$1(angle, 1);
|
|
32056
|
+
})) {
|
|
32057
|
+
return startingPoint;
|
|
32058
|
+
}
|
|
32059
|
+
const knownSide = magnitude(
|
|
32060
|
+
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
32061
|
+
vector(coordsCopy[rel(-1)], coordsCopy[rel(1)]));
|
|
32062
|
+
|
|
32063
|
+
// Returns true if the points form a counter-clockwise turn;
|
|
32064
|
+
// a.k.a if the point is on the left or right of the polygon.
|
|
32065
|
+
// This is used to determine how to adjust the point: if the point is on the left,
|
|
32066
|
+
// we want to add the inner angle to the outer angle, and if it's on the right,
|
|
32067
|
+
// we want to subtract the inner angle from the outer angle. The angle solved
|
|
32068
|
+
// for is then used in the polar function to determine the new point.
|
|
32069
|
+
const onLeft = sign(ccw(coordsCopy[rel(-1)], coordsCopy[rel(1)], coordsCopy[index])) === 1;
|
|
32070
|
+
|
|
32071
|
+
// Solve for side by using the law of sines
|
|
32072
|
+
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
32073
|
+
|
|
32074
|
+
// Angle at the second vertex of the polygon
|
|
32075
|
+
const outerAngle = GraphUtils.findAngle(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
32076
|
+
|
|
32077
|
+
// Uses the length of the side of the polygon (radial coordinate)
|
|
32078
|
+
// and the angle between the first and second sides of the
|
|
32079
|
+
// polygon (angular coordinate) to determine how to adjust the point
|
|
32080
|
+
const offset = polar(side, outerAngle + (onLeft ? 1 : -1) * innerAngles[0]);
|
|
32081
|
+
return kmath.vector.add(coordsCopy[rel(-1)], offset);
|
|
32082
|
+
}
|
|
32083
|
+
function boundAndSnapToSides(destinationPoint, _ref3, index) {
|
|
32084
|
+
let {
|
|
32085
|
+
range,
|
|
32086
|
+
coords
|
|
32087
|
+
} = _ref3;
|
|
32088
|
+
const startingPoint = coords[index];
|
|
32089
|
+
|
|
32090
|
+
// Needed to prevent updating the original coords before the checks for
|
|
32091
|
+
// degenerate triangles and overlapping sides
|
|
32092
|
+
const coordsCopy = [...coords];
|
|
32093
|
+
|
|
32094
|
+
// Takes the destination point and makes sure it is within the bounds of the graph
|
|
32095
|
+
// SnapStep is [0, 0] because we don't want to snap to the grid
|
|
32096
|
+
coordsCopy[index] = bound$1({
|
|
32097
|
+
snapStep: [0, 0],
|
|
32098
|
+
range,
|
|
32099
|
+
point: destinationPoint
|
|
32100
|
+
});
|
|
32101
|
+
|
|
32102
|
+
// Gets the relative index of a point
|
|
32103
|
+
const rel = j => {
|
|
32104
|
+
return (index + j + coordsCopy.length) % coordsCopy.length;
|
|
32105
|
+
};
|
|
32106
|
+
const sides = ___default["default"].map([[coordsCopy[rel(-1)], coordsCopy[index]], [coordsCopy[index], coordsCopy[rel(1)]], [coordsCopy[rel(-1)], coordsCopy[rel(1)]]], function (coordsCopy) {
|
|
32107
|
+
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
32108
|
+
return magnitude(vector(...coordsCopy));
|
|
32109
|
+
});
|
|
32110
|
+
|
|
32111
|
+
// Round the sides to left and right of the current point
|
|
32112
|
+
___default["default"].each([0, 1], function (j) {
|
|
32113
|
+
sides[j] = Math.round(sides[j]);
|
|
32114
|
+
});
|
|
32115
|
+
|
|
32116
|
+
// Avoid degenerate triangles
|
|
32117
|
+
if (leq$1(sides[1] + sides[2], sides[0]) || leq$1(sides[0] + sides[2], sides[1]) || leq$1(sides[0] + sides[1], sides[2])) {
|
|
32118
|
+
return startingPoint;
|
|
32119
|
+
}
|
|
32120
|
+
|
|
32121
|
+
// Solve for angle by using the law of cosines
|
|
32122
|
+
// Angle at the first vertex of the polygon
|
|
32123
|
+
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
32124
|
+
|
|
32125
|
+
// Angle at the second vertex of the polygon
|
|
32126
|
+
const outerAngle = GraphUtils.findAngle(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
32127
|
+
|
|
32128
|
+
// Returns true if the points form a counter-clockwise turn;
|
|
32129
|
+
// a.k.a if the point is on the left or right of the polygon.
|
|
32130
|
+
// This is used to determine how to adjust the point: if the point is on the left,
|
|
32131
|
+
// we want to add the inner angle to the outer angle, and if it's on the right,
|
|
32132
|
+
// we want to subtract the inner angle from the outer angle. The angle solved
|
|
32133
|
+
// for is then used in the polar function to determine the new point.
|
|
32134
|
+
const onLeft = sign(ccw(coordsCopy[rel(-1)], coordsCopy[rel(1)], coordsCopy[index])) === 1;
|
|
32135
|
+
|
|
32136
|
+
// Uses the length of the first side of the polygon (radial coordinate)
|
|
32137
|
+
// and the angle between the first and second sides of the
|
|
32138
|
+
// polygon (angular coordinate) to determine how to adjust the point
|
|
32139
|
+
const offset = polar(sides[0], outerAngle + (onLeft ? 1 : -1) * innerAngle);
|
|
32140
|
+
return kmath.vector.add(coordsCopy[rel(-1)], offset);
|
|
32141
|
+
}
|
|
32000
32142
|
|
|
32001
32143
|
// Returns the closest point to the given `point` that is within the graph
|
|
32002
32144
|
// bounds given in `state`.
|
|
32003
|
-
function bound$1(
|
|
32145
|
+
function bound$1(_ref4) {
|
|
32004
32146
|
let {
|
|
32005
32147
|
snapStep,
|
|
32006
32148
|
range,
|
|
32007
32149
|
point
|
|
32008
|
-
} =
|
|
32150
|
+
} = _ref4;
|
|
32009
32151
|
const [requestedX, requestedY] = point;
|
|
32010
32152
|
const [snapX, snapY] = snapStep;
|
|
32011
32153
|
const [[minX, maxX], [minY, maxY]] = range;
|
|
32012
32154
|
return [clamp(requestedX, minX + snapX, maxX - snapX), clamp(requestedY, minY + snapY, maxY - snapY)];
|
|
32013
32155
|
}
|
|
32014
32156
|
|
|
32015
|
-
// Returns the vector from the given point to the top-right corner of the graph
|
|
32016
|
-
function maxMove(
|
|
32157
|
+
// Returns the vector from the given point to the top-right corner of the graph when snapped to the grid
|
|
32158
|
+
function maxMove(_ref5) {
|
|
32017
32159
|
let {
|
|
32018
32160
|
snapStep,
|
|
32019
32161
|
range,
|
|
32020
32162
|
point
|
|
32021
|
-
} =
|
|
32163
|
+
} = _ref5;
|
|
32022
32164
|
const topRight = bound$1({
|
|
32023
32165
|
snapStep,
|
|
32024
32166
|
range,
|
|
@@ -32027,14 +32169,13 @@ function maxMove(_ref3) {
|
|
|
32027
32169
|
return mafs.vec.sub(topRight, point);
|
|
32028
32170
|
}
|
|
32029
32171
|
|
|
32030
|
-
// Returns the vector from the given point to the bottom-left corner of the
|
|
32031
|
-
|
|
32032
|
-
function minMove(_ref4) {
|
|
32172
|
+
// Returns the vector from the given point to the bottom-left corner of the graph when snapped to the grid
|
|
32173
|
+
function minMove(_ref6) {
|
|
32033
32174
|
let {
|
|
32034
32175
|
snapStep,
|
|
32035
32176
|
range,
|
|
32036
32177
|
point
|
|
32037
|
-
} =
|
|
32178
|
+
} = _ref6;
|
|
32038
32179
|
const bottomLeft = bound$1({
|
|
32039
32180
|
snapStep,
|
|
32040
32181
|
range,
|
|
@@ -32155,11 +32296,12 @@ function mafsStateToInteractiveGraph(state) {
|
|
|
32155
32296
|
}
|
|
32156
32297
|
const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
|
|
32157
32298
|
const {
|
|
32158
|
-
onChange
|
|
32299
|
+
onChange,
|
|
32300
|
+
graph
|
|
32159
32301
|
} = props;
|
|
32160
32302
|
const [state, dispatch] = React__namespace.useReducer(interactiveGraphReducer, props, initializeGraphState);
|
|
32161
32303
|
React.useImperativeHandle(ref, () => ({
|
|
32162
|
-
getUserInput: () => getGradableGraph(state,
|
|
32304
|
+
getUserInput: () => getGradableGraph(state, graph)
|
|
32163
32305
|
}));
|
|
32164
32306
|
const prevState = React.useRef(state);
|
|
32165
32307
|
React.useEffect(() => {
|
|
@@ -32182,6 +32324,18 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
|
|
|
32182
32324
|
React.useEffect(() => {
|
|
32183
32325
|
dispatch(changeRange([[xMinRange, xMaxRange], [yMinRange, yMaxRange]]));
|
|
32184
32326
|
}, [dispatch, xMinRange, xMaxRange, yMinRange, yMaxRange]);
|
|
32327
|
+
const numSegments = graph.type === "segment" ? graph.numSegments : null;
|
|
32328
|
+
const originalPropsRef = React.useRef(props);
|
|
32329
|
+
const latestPropsRef = wonderBlocksCore.useLatestRef(props);
|
|
32330
|
+
React.useEffect(() => {
|
|
32331
|
+
// This conditional prevents the state from being "reinitialized" right
|
|
32332
|
+
// after the first render. This is an optimization, but also prevents
|
|
32333
|
+
// a bug where the graph would be marked "incorrect" during grading
|
|
32334
|
+
// even if the user never interacted with it.
|
|
32335
|
+
if (latestPropsRef.current !== originalPropsRef.current) {
|
|
32336
|
+
dispatch(reinitialize(latestPropsRef.current));
|
|
32337
|
+
}
|
|
32338
|
+
}, [graph.type, numSegments, latestPropsRef]);
|
|
32185
32339
|
return /*#__PURE__*/React__namespace.createElement(MafsGraph, _extends({}, props, {
|
|
32186
32340
|
state: state,
|
|
32187
32341
|
dispatch: dispatch
|
|
@@ -44343,7 +44497,7 @@ const init = function (options) {
|
|
|
44343
44497
|
|
|
44344
44498
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44345
44499
|
const libName = "@khanacademy/perseus";
|
|
44346
|
-
const libVersion = "23.
|
|
44500
|
+
const libVersion = "23.5.0";
|
|
44347
44501
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44348
44502
|
|
|
44349
44503
|
const apiVersion = {
|
|
@@ -47443,8 +47597,8 @@ exports.itemDataVersion = itemDataVersion;
|
|
|
47443
47597
|
exports.itemToTree = itemToTree;
|
|
47444
47598
|
exports.itemVersion = ItemVersion;
|
|
47445
47599
|
exports.libVersion = libVersion;
|
|
47446
|
-
exports.lockedEllipseFillStyles = lockedEllipseFillStyles;
|
|
47447
47600
|
exports.lockedFigureColors = lockedFigureColors;
|
|
47601
|
+
exports.lockedFigureFillStyles = lockedFigureFillStyles;
|
|
47448
47602
|
exports.mediaQueries = mediaQueries;
|
|
47449
47603
|
exports.parsePerseusItem = parsePerseusItem;
|
|
47450
47604
|
exports.plotterPlotTypes = plotterPlotTypes;
|