@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/es/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import * as KAS from '@khanacademy/kas';
|
|
|
2
2
|
import { MathInputI18nContext, CursorContext, getKeyTranslator, createMathField, getCursorContext, DesktopKeypad, convertDotToTimesByLocale, mathQuillInstance, KeypadInput, KeypadType, KeyArray, keypadElementPropType } from '@khanacademy/math-input';
|
|
3
3
|
import * as PerseusLinter from '@khanacademy/perseus-linter';
|
|
4
4
|
import { linterContextDefault, Rule, linterContextProps } from '@khanacademy/perseus-linter';
|
|
5
|
-
import { View, RenderStateRoot, UniqueIDProvider, useUniqueIdWithMock, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
|
|
5
|
+
import { View, RenderStateRoot, UniqueIDProvider, useLatestRef, useUniqueIdWithMock, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
|
|
6
6
|
import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
|
|
7
7
|
import classNames from 'classnames';
|
|
8
8
|
import * as React from 'react';
|
|
@@ -29,7 +29,7 @@ import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-block
|
|
|
29
29
|
import caretDown from '@phosphor-icons/core/regular/caret-down.svg';
|
|
30
30
|
import caretUp from '@phosphor-icons/core/regular/caret-up.svg';
|
|
31
31
|
import debounce$2 from 'lodash.debounce';
|
|
32
|
-
import { vec, Ellipse, Line as Line$4, Point as Point$2, useMovable, Text as Text$1,
|
|
32
|
+
import { vec, Ellipse, Line as Line$4, Point as Point$2, Polygon, useMovable, Text as Text$1, Plot as Plot$3, Coordinates, Mafs } from 'mafs';
|
|
33
33
|
import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
|
|
34
34
|
import { Body, LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
|
|
35
35
|
import questionIcon from '@phosphor-icons/core/regular/question.svg';
|
|
@@ -3202,7 +3202,7 @@ const DeprecationMixin$1 = {
|
|
|
3202
3202
|
/**
|
|
3203
3203
|
* Approximate equality on numbers and primitives.
|
|
3204
3204
|
*/
|
|
3205
|
-
function eq$
|
|
3205
|
+
function eq$3(x, y) {
|
|
3206
3206
|
if (typeof x === "number" && typeof y === "number") {
|
|
3207
3207
|
return Math.abs(x - y) < 1e-9;
|
|
3208
3208
|
}
|
|
@@ -3229,7 +3229,7 @@ function deepEq$5(x, y) {
|
|
|
3229
3229
|
return false;
|
|
3230
3230
|
}
|
|
3231
3231
|
if (typeof x === "function" && typeof y === "function") {
|
|
3232
|
-
return eq$
|
|
3232
|
+
return eq$3(x, y);
|
|
3233
3233
|
}
|
|
3234
3234
|
if (typeof x === "function" || typeof y === "function") {
|
|
3235
3235
|
return false;
|
|
@@ -3246,7 +3246,7 @@ function deepEq$5(x, y) {
|
|
|
3246
3246
|
if (typeof x === "object" && !!x || typeof y === "object" && !!y) {
|
|
3247
3247
|
return false;
|
|
3248
3248
|
}
|
|
3249
|
-
return eq$
|
|
3249
|
+
return eq$3(x, y);
|
|
3250
3250
|
}
|
|
3251
3251
|
|
|
3252
3252
|
/**
|
|
@@ -3547,7 +3547,7 @@ const Util = {
|
|
|
3547
3547
|
tickStepFromNumTicks,
|
|
3548
3548
|
constrainedTickStepsFromTickSteps,
|
|
3549
3549
|
DeprecationMixin: DeprecationMixin$1,
|
|
3550
|
-
eq: eq$
|
|
3550
|
+
eq: eq$3,
|
|
3551
3551
|
deepEq: deepEq$5,
|
|
3552
3552
|
parseQueryString,
|
|
3553
3553
|
updateQueryString: updateQueryString$2,
|
|
@@ -10784,7 +10784,7 @@ class WrappedLine extends WrappedDrawing {
|
|
|
10784
10784
|
* A collection of geomtry-related utility functions
|
|
10785
10785
|
*/
|
|
10786
10786
|
const {
|
|
10787
|
-
eq: eq$
|
|
10787
|
+
eq: eq$2,
|
|
10788
10788
|
deepEq: deepEq$4
|
|
10789
10789
|
} = Util;
|
|
10790
10790
|
|
|
@@ -10792,17 +10792,21 @@ const {
|
|
|
10792
10792
|
|
|
10793
10793
|
// a, b, c
|
|
10794
10794
|
|
|
10795
|
+
// Given a number, return whether it is positive (1), negative (-1), or zero (0)
|
|
10795
10796
|
function sign(val) {
|
|
10796
|
-
if (eq$
|
|
10797
|
+
if (eq$2(val, 0)) {
|
|
10797
10798
|
return 0;
|
|
10798
10799
|
}
|
|
10799
10800
|
return val > 0 ? 1 : -1;
|
|
10800
10801
|
}
|
|
10802
|
+
|
|
10803
|
+
// Determine whether three points are collinear (0), for a clockwise turn (negative),
|
|
10804
|
+
// or counterclockwise turn (positive)
|
|
10801
10805
|
function ccw(a, b, c) {
|
|
10802
10806
|
return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
|
|
10803
10807
|
}
|
|
10804
10808
|
function collinear(a, b, c) {
|
|
10805
|
-
return eq$
|
|
10809
|
+
return eq$2(ccw(a, b, c), 0);
|
|
10806
10810
|
}
|
|
10807
10811
|
|
|
10808
10812
|
// Given rect bounding points A and B, whether point C is inside the rect
|
|
@@ -10947,7 +10951,7 @@ function similar(coords1, coords2, tolerance) {
|
|
|
10947
10951
|
return pair[0] / pair[1];
|
|
10948
10952
|
});
|
|
10949
10953
|
const same = _.all(factors, function (factor) {
|
|
10950
|
-
return eq$
|
|
10954
|
+
return eq$2(factors[0], factor);
|
|
10951
10955
|
});
|
|
10952
10956
|
const congruentEnough = _.all(sidePairs, function (pair) {
|
|
10953
10957
|
return number.equal(pair[0], pair[1], tolerance);
|
|
@@ -10999,7 +11003,7 @@ function rotate(array, n) {
|
|
|
10999
11003
|
return array.slice(n).concat(array.slice(0, n));
|
|
11000
11004
|
}
|
|
11001
11005
|
function getLineEquation(first, second) {
|
|
11002
|
-
if (eq$
|
|
11006
|
+
if (eq$2(first[0], second[0])) {
|
|
11003
11007
|
return "x = " + first[0].toFixed(3);
|
|
11004
11008
|
}
|
|
11005
11009
|
const m = (second[1] - first[1]) / (second[0] - first[0]);
|
|
@@ -28652,13 +28656,15 @@ const ItemExtras = [
|
|
|
28652
28656
|
|
|
28653
28657
|
const PerseusExpressionAnswerFormConsidered = ["correct", "wrong", "ungraded"];
|
|
28654
28658
|
const lockedFigureColors = {
|
|
28659
|
+
blue: "#3D7586",
|
|
28655
28660
|
green: "#447A53",
|
|
28656
28661
|
grayH: "#3B3D45",
|
|
28657
28662
|
purple: "#594094",
|
|
28658
28663
|
pink: "#B25071",
|
|
28659
|
-
red: "#D92916"
|
|
28664
|
+
red: "#D92916",
|
|
28665
|
+
orange: "#946700"
|
|
28660
28666
|
};
|
|
28661
|
-
const
|
|
28667
|
+
const lockedFigureFillStyles = {
|
|
28662
28668
|
none: 0,
|
|
28663
28669
|
solid: 1,
|
|
28664
28670
|
translucent: 0.4
|
|
@@ -28678,7 +28684,7 @@ const LockedEllipse = props => {
|
|
|
28678
28684
|
center: center,
|
|
28679
28685
|
radius: radius,
|
|
28680
28686
|
angle: angle,
|
|
28681
|
-
fillOpacity:
|
|
28687
|
+
fillOpacity: lockedFigureFillStyles[fillStyle],
|
|
28682
28688
|
strokeStyle: strokeStyle,
|
|
28683
28689
|
color: lockedFigureColors[color]
|
|
28684
28690
|
});
|
|
@@ -28938,6 +28944,29 @@ const LockedPoint = props => {
|
|
|
28938
28944
|
});
|
|
28939
28945
|
};
|
|
28940
28946
|
|
|
28947
|
+
const LockedPolygon = props => {
|
|
28948
|
+
const {
|
|
28949
|
+
points,
|
|
28950
|
+
color,
|
|
28951
|
+
showVertices,
|
|
28952
|
+
fillStyle,
|
|
28953
|
+
strokeStyle
|
|
28954
|
+
} = props;
|
|
28955
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
28956
|
+
className: "locked-polygon"
|
|
28957
|
+
}, /*#__PURE__*/React.createElement(Polygon, {
|
|
28958
|
+
points: [...points],
|
|
28959
|
+
fillOpacity: lockedFigureFillStyles[fillStyle],
|
|
28960
|
+
strokeStyle: strokeStyle,
|
|
28961
|
+
color: lockedFigureColors[color]
|
|
28962
|
+
}), showVertices && points.map((point, index) => /*#__PURE__*/React.createElement(Point$2, {
|
|
28963
|
+
key: `locked-polygon-point-${index}`,
|
|
28964
|
+
x: point[0],
|
|
28965
|
+
y: point[1],
|
|
28966
|
+
color: lockedFigureColors[color]
|
|
28967
|
+
})));
|
|
28968
|
+
};
|
|
28969
|
+
|
|
28941
28970
|
const LockedVector = props => {
|
|
28942
28971
|
const {
|
|
28943
28972
|
color,
|
|
@@ -28968,13 +28997,17 @@ const GraphLockedLayer = props => {
|
|
|
28968
28997
|
key: `line-${index}`,
|
|
28969
28998
|
range: props.range
|
|
28970
28999
|
}, figure));
|
|
29000
|
+
case "vector":
|
|
29001
|
+
return /*#__PURE__*/React.createElement(LockedVector, _extends({
|
|
29002
|
+
key: `vector-${index}`
|
|
29003
|
+
}, figure));
|
|
28971
29004
|
case "ellipse":
|
|
28972
29005
|
return /*#__PURE__*/React.createElement(LockedEllipse, _extends({
|
|
28973
29006
|
key: `ellipse-${index}`
|
|
28974
29007
|
}, figure));
|
|
28975
|
-
case "
|
|
28976
|
-
return /*#__PURE__*/React.createElement(
|
|
28977
|
-
key: `
|
|
29008
|
+
case "polygon":
|
|
29009
|
+
return /*#__PURE__*/React.createElement(LockedPolygon, _extends({
|
|
29010
|
+
key: `polygon-${index}`
|
|
28978
29011
|
}, figure));
|
|
28979
29012
|
default:
|
|
28980
29013
|
/**
|
|
@@ -28988,6 +29021,13 @@ const GraphLockedLayer = props => {
|
|
|
28988
29021
|
}));
|
|
28989
29022
|
};
|
|
28990
29023
|
|
|
29024
|
+
const REINITIALIZE = "reinitialize";
|
|
29025
|
+
function reinitialize(params) {
|
|
29026
|
+
return {
|
|
29027
|
+
type: REINITIALIZE,
|
|
29028
|
+
params
|
|
29029
|
+
};
|
|
29030
|
+
}
|
|
28991
29031
|
const MOVE_CONTROL_POINT = "move-control-point";
|
|
28992
29032
|
function moveControlPoint(pointIndex, destination, itemIndex) {
|
|
28993
29033
|
return {
|
|
@@ -29250,6 +29290,7 @@ function useControlPoint(point, color, onMovePoint) {
|
|
|
29250
29290
|
});
|
|
29251
29291
|
const focusableHandle = /*#__PURE__*/React.createElement("g", {
|
|
29252
29292
|
"data-testid": "movable-point__focusable-handle",
|
|
29293
|
+
className: "movable-point__focusable-handle",
|
|
29253
29294
|
tabIndex: 0,
|
|
29254
29295
|
ref: keyboardHandleRef,
|
|
29255
29296
|
onFocus: () => setFocused(true),
|
|
@@ -29419,7 +29460,6 @@ const LinearSystemGraph = props => {
|
|
|
29419
29460
|
const {
|
|
29420
29461
|
coords: lines
|
|
29421
29462
|
} = props.graphState;
|
|
29422
|
-
const colors = ["var(--movable-line-stroke-color)", "var(--mafs-violet)"];
|
|
29423
29463
|
return /*#__PURE__*/React.createElement(React.Fragment, null, lines == null ? void 0 : lines.map((line, i) => /*#__PURE__*/React.createElement(MovableLine, {
|
|
29424
29464
|
key: i,
|
|
29425
29465
|
points: line,
|
|
@@ -29431,7 +29471,7 @@ const LinearSystemGraph = props => {
|
|
|
29431
29471
|
end: true
|
|
29432
29472
|
},
|
|
29433
29473
|
onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i)),
|
|
29434
|
-
color:
|
|
29474
|
+
color: "var(--movable-line-stroke-color)"
|
|
29435
29475
|
})), ";");
|
|
29436
29476
|
};
|
|
29437
29477
|
|
|
@@ -29503,7 +29543,8 @@ const Angle = ({
|
|
|
29503
29543
|
endPoints,
|
|
29504
29544
|
range,
|
|
29505
29545
|
polygonLines,
|
|
29506
|
-
showAngles
|
|
29546
|
+
showAngles,
|
|
29547
|
+
snapTo
|
|
29507
29548
|
}) => {
|
|
29508
29549
|
const [centerX, centerY] = centerPoint;
|
|
29509
29550
|
const areClockwise = clockwise$1([centerPoint, ...endPoints]);
|
|
@@ -29541,7 +29582,9 @@ const Angle = ({
|
|
|
29541
29582
|
if (isOutside) {
|
|
29542
29583
|
angleInDegrees = 360 - angleInDegrees;
|
|
29543
29584
|
}
|
|
29544
|
-
|
|
29585
|
+
|
|
29586
|
+
// Only want to show whole numbers when in angles mode
|
|
29587
|
+
const angleLabelNumber = parseFloat(angleInDegrees.toFixed(snapTo === "angles" ? 0 : 1));
|
|
29545
29588
|
const angleLabel = Number.isInteger(angleLabelNumber) ? angleLabelNumber : "≈ " + angleLabelNumber;
|
|
29546
29589
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("filter", {
|
|
29547
29590
|
id: "background",
|
|
@@ -29627,15 +29670,17 @@ const StyledMovablePoint = props => {
|
|
|
29627
29670
|
point,
|
|
29628
29671
|
onMove,
|
|
29629
29672
|
cursor,
|
|
29630
|
-
color: color$1 = color.blue
|
|
29673
|
+
color: color$1 = color.blue,
|
|
29674
|
+
snapTo
|
|
29631
29675
|
} = props;
|
|
29676
|
+
const snapToValue = snapTo != null ? snapTo : "grid";
|
|
29632
29677
|
const {
|
|
29633
29678
|
dragging
|
|
29634
29679
|
} = useMovable({
|
|
29635
29680
|
gestureTarget: elementRef,
|
|
29636
29681
|
point,
|
|
29637
29682
|
onMove,
|
|
29638
|
-
constrain: p => snap(snapStep, p)
|
|
29683
|
+
constrain: p => ["angles", "sides"].includes(snapToValue) ? p : snap(snapStep, p)
|
|
29639
29684
|
});
|
|
29640
29685
|
return /*#__PURE__*/React.createElement(MovablePointView, {
|
|
29641
29686
|
ref: elementRef,
|
|
@@ -29661,7 +29706,8 @@ const PolygonGraph = props => {
|
|
|
29661
29706
|
showAngles,
|
|
29662
29707
|
showSides,
|
|
29663
29708
|
range,
|
|
29664
|
-
snapStep
|
|
29709
|
+
snapStep,
|
|
29710
|
+
snapTo
|
|
29665
29711
|
} = props.graphState;
|
|
29666
29712
|
|
|
29667
29713
|
// TODO(benchristel): can the default set of points be removed here? I don't
|
|
@@ -29669,6 +29715,7 @@ const PolygonGraph = props => {
|
|
|
29669
29715
|
const points = coords != null ? coords : [[0, 0]];
|
|
29670
29716
|
const ref = React.useRef(null);
|
|
29671
29717
|
const dragReferencePoint = points[0];
|
|
29718
|
+
const snapToValue = snapTo != null ? snapTo : "grid";
|
|
29672
29719
|
const {
|
|
29673
29720
|
dragging
|
|
29674
29721
|
} = useMovable({
|
|
@@ -29678,7 +29725,7 @@ const PolygonGraph = props => {
|
|
|
29678
29725
|
const delta = vec.sub(newPoint, dragReferencePoint);
|
|
29679
29726
|
dispatch(moveAll(delta));
|
|
29680
29727
|
},
|
|
29681
|
-
constrain: p => snap(snapStep, p)
|
|
29728
|
+
constrain: p => ["angles", "sides"].includes(snapToValue) ? p : snap(snapStep, p)
|
|
29682
29729
|
});
|
|
29683
29730
|
const active = hovered || focused || dragging;
|
|
29684
29731
|
const lines = getLines(points);
|
|
@@ -29701,11 +29748,12 @@ const PolygonGraph = props => {
|
|
|
29701
29748
|
active: active,
|
|
29702
29749
|
range: range,
|
|
29703
29750
|
polygonLines: lines,
|
|
29704
|
-
showAngles: !!showAngles
|
|
29751
|
+
showAngles: !!showAngles,
|
|
29752
|
+
snapTo: snapToValue
|
|
29705
29753
|
});
|
|
29706
29754
|
}), showSides && lines.map(([start, end], i) => {
|
|
29707
29755
|
const [x, y] = vec.midpoint(start, end);
|
|
29708
|
-
const length = parseFloat(vec.dist(start, end).toFixed(1));
|
|
29756
|
+
const length = parseFloat(vec.dist(start, end).toFixed(snapTo === "sides" ? 0 : 1));
|
|
29709
29757
|
return /*#__PURE__*/React.createElement(TextLabel, {
|
|
29710
29758
|
key: "side-" + i,
|
|
29711
29759
|
x: x,
|
|
@@ -29729,143 +29777,12 @@ const PolygonGraph = props => {
|
|
|
29729
29777
|
}
|
|
29730
29778
|
}), points.map((point, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
29731
29779
|
key: "point-" + i,
|
|
29780
|
+
snapTo: snapTo,
|
|
29732
29781
|
point: point,
|
|
29733
29782
|
onMove: destination => dispatch(movePoint(i, destination))
|
|
29734
29783
|
})));
|
|
29735
29784
|
};
|
|
29736
29785
|
|
|
29737
|
-
function initializeGraphState(params) {
|
|
29738
|
-
const {
|
|
29739
|
-
graph,
|
|
29740
|
-
step,
|
|
29741
|
-
snapStep,
|
|
29742
|
-
range
|
|
29743
|
-
} = params;
|
|
29744
|
-
const shared = {
|
|
29745
|
-
hasBeenInteractedWith: false,
|
|
29746
|
-
range,
|
|
29747
|
-
snapStep
|
|
29748
|
-
};
|
|
29749
|
-
switch (graph.type) {
|
|
29750
|
-
case "segment":
|
|
29751
|
-
return _extends({}, shared, {
|
|
29752
|
-
type: "segment",
|
|
29753
|
-
coords: getSegmentCoords({
|
|
29754
|
-
graph,
|
|
29755
|
-
step,
|
|
29756
|
-
range
|
|
29757
|
-
})
|
|
29758
|
-
});
|
|
29759
|
-
case "linear":
|
|
29760
|
-
return _extends({}, shared, {
|
|
29761
|
-
type: graph.type,
|
|
29762
|
-
coords: getLineCoords({
|
|
29763
|
-
graph,
|
|
29764
|
-
range,
|
|
29765
|
-
step
|
|
29766
|
-
})
|
|
29767
|
-
});
|
|
29768
|
-
case "ray":
|
|
29769
|
-
return _extends({}, shared, {
|
|
29770
|
-
type: graph.type,
|
|
29771
|
-
coords: getLineCoords({
|
|
29772
|
-
graph,
|
|
29773
|
-
range,
|
|
29774
|
-
step
|
|
29775
|
-
})
|
|
29776
|
-
});
|
|
29777
|
-
case "linear-system":
|
|
29778
|
-
return _extends({}, shared, {
|
|
29779
|
-
type: graph.type,
|
|
29780
|
-
coords: getLinearSystemCoords({
|
|
29781
|
-
graph,
|
|
29782
|
-
range,
|
|
29783
|
-
step
|
|
29784
|
-
})
|
|
29785
|
-
});
|
|
29786
|
-
case "polygon":
|
|
29787
|
-
return _extends({}, shared, {
|
|
29788
|
-
type: "polygon",
|
|
29789
|
-
showAngles: Boolean(graph.showAngles),
|
|
29790
|
-
showSides: Boolean(graph.showSides),
|
|
29791
|
-
coords: getPolygonCoords({
|
|
29792
|
-
graph,
|
|
29793
|
-
range,
|
|
29794
|
-
step
|
|
29795
|
-
})
|
|
29796
|
-
});
|
|
29797
|
-
case "point":
|
|
29798
|
-
return _extends({}, shared, {
|
|
29799
|
-
type: graph.type,
|
|
29800
|
-
coords: getDefaultPoints({
|
|
29801
|
-
graph,
|
|
29802
|
-
step,
|
|
29803
|
-
range
|
|
29804
|
-
})
|
|
29805
|
-
});
|
|
29806
|
-
case "circle":
|
|
29807
|
-
return _extends({}, shared, {
|
|
29808
|
-
type: graph.type
|
|
29809
|
-
}, getCircleCoords(graph));
|
|
29810
|
-
case "quadratic":
|
|
29811
|
-
return _extends({}, shared, {
|
|
29812
|
-
type: graph.type,
|
|
29813
|
-
coords: getQuadraticCoords(graph, range, step)
|
|
29814
|
-
});
|
|
29815
|
-
case "sinusoid":
|
|
29816
|
-
return _extends({}, shared, {
|
|
29817
|
-
type: graph.type,
|
|
29818
|
-
coords: getSinusoidCoords(graph, range, step)
|
|
29819
|
-
});
|
|
29820
|
-
case "angle":
|
|
29821
|
-
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
29822
|
-
default:
|
|
29823
|
-
throw new UnreachableCaseError(graph);
|
|
29824
|
-
}
|
|
29825
|
-
}
|
|
29826
|
-
const getDefaultPoints = ({
|
|
29827
|
-
graph,
|
|
29828
|
-
range,
|
|
29829
|
-
step
|
|
29830
|
-
}) => {
|
|
29831
|
-
var _graph$coords;
|
|
29832
|
-
const numPoints = graph.numPoints || 1;
|
|
29833
|
-
let coords = (_graph$coords = graph.coords) == null ? void 0 : _graph$coords.slice();
|
|
29834
|
-
if (coords) {
|
|
29835
|
-
return coords;
|
|
29836
|
-
}
|
|
29837
|
-
switch (numPoints) {
|
|
29838
|
-
case 1:
|
|
29839
|
-
// Back in the day, one point's coords were in graph.coord
|
|
29840
|
-
coords = [graph.coord || [0, 0]];
|
|
29841
|
-
break;
|
|
29842
|
-
case 2:
|
|
29843
|
-
coords = [[-5, 0], [5, 0]];
|
|
29844
|
-
break;
|
|
29845
|
-
case 3:
|
|
29846
|
-
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
29847
|
-
break;
|
|
29848
|
-
case 4:
|
|
29849
|
-
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
29850
|
-
break;
|
|
29851
|
-
case 5:
|
|
29852
|
-
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
29853
|
-
break;
|
|
29854
|
-
case 6:
|
|
29855
|
-
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
29856
|
-
break;
|
|
29857
|
-
default:
|
|
29858
|
-
coords = [];
|
|
29859
|
-
break;
|
|
29860
|
-
}
|
|
29861
|
-
// Transform coords from their -10 to 10 space to 0 to 1
|
|
29862
|
-
// because of the old graph.coord, and also it's easier.
|
|
29863
|
-
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
29864
|
-
return normalizePoints(range, step, newCoords);
|
|
29865
|
-
};
|
|
29866
|
-
|
|
29867
|
-
// TS v4 doesn't narrow return types, while v5 does.
|
|
29868
|
-
// Instead of updating to v5, using generic type to relate input and output types.
|
|
29869
29786
|
function getGradableGraph(state, initialGraph) {
|
|
29870
29787
|
if (!state.hasBeenInteractedWith) {
|
|
29871
29788
|
return _extends({}, initialGraph);
|
|
@@ -29918,115 +29835,6 @@ function getGradableGraph(state, initialGraph) {
|
|
|
29918
29835
|
}
|
|
29919
29836
|
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
29920
29837
|
}
|
|
29921
|
-
const getSegmentCoords = ({
|
|
29922
|
-
graph,
|
|
29923
|
-
range,
|
|
29924
|
-
step
|
|
29925
|
-
}) => {
|
|
29926
|
-
if (graph.coords) {
|
|
29927
|
-
return graph.coords;
|
|
29928
|
-
}
|
|
29929
|
-
const ys = n => {
|
|
29930
|
-
switch (n) {
|
|
29931
|
-
case 2:
|
|
29932
|
-
return [5, -5];
|
|
29933
|
-
case 3:
|
|
29934
|
-
return [5, 0, -5];
|
|
29935
|
-
case 4:
|
|
29936
|
-
return [6, 2, -2, -6];
|
|
29937
|
-
case 5:
|
|
29938
|
-
return [6, 3, 0, -3, -6];
|
|
29939
|
-
case 6:
|
|
29940
|
-
return [5, 3, 1, -1, -3, -5];
|
|
29941
|
-
default:
|
|
29942
|
-
return [5];
|
|
29943
|
-
}
|
|
29944
|
-
};
|
|
29945
|
-
const defaultRange = [[-10, 10], [-10, 10]];
|
|
29946
|
-
return ys(graph.numSegments).map(y => {
|
|
29947
|
-
let endpoints = [[-5, y], [5, y]];
|
|
29948
|
-
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
29949
|
-
endpoints = normalizePoints(range, step, endpoints);
|
|
29950
|
-
return endpoints;
|
|
29951
|
-
});
|
|
29952
|
-
};
|
|
29953
|
-
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
29954
|
-
const getLineCoords = ({
|
|
29955
|
-
graph,
|
|
29956
|
-
range,
|
|
29957
|
-
step
|
|
29958
|
-
}) => {
|
|
29959
|
-
if (graph.coords) {
|
|
29960
|
-
return graph.coords;
|
|
29961
|
-
}
|
|
29962
|
-
return normalizePoints(range, step, defaultLinearCoords[0]);
|
|
29963
|
-
};
|
|
29964
|
-
const getLinearSystemCoords = ({
|
|
29965
|
-
graph,
|
|
29966
|
-
range,
|
|
29967
|
-
step
|
|
29968
|
-
}) => {
|
|
29969
|
-
if (graph.coords) {
|
|
29970
|
-
return graph.coords;
|
|
29971
|
-
}
|
|
29972
|
-
return defaultLinearCoords.map(points => normalizePoints(range, step, points));
|
|
29973
|
-
};
|
|
29974
|
-
const getPolygonCoords = ({
|
|
29975
|
-
graph,
|
|
29976
|
-
range,
|
|
29977
|
-
step
|
|
29978
|
-
}) => {
|
|
29979
|
-
var _graph$coords2;
|
|
29980
|
-
let coords = (_graph$coords2 = graph.coords) == null ? void 0 : _graph$coords2.slice();
|
|
29981
|
-
if (coords) {
|
|
29982
|
-
return coords;
|
|
29983
|
-
}
|
|
29984
|
-
const n = graph.numSides || 3;
|
|
29985
|
-
if (n === "unlimited") {
|
|
29986
|
-
coords = [];
|
|
29987
|
-
} else {
|
|
29988
|
-
const angle = 2 * Math.PI / n;
|
|
29989
|
-
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
29990
|
-
|
|
29991
|
-
// TODO(alex): Generalize this to more than just triangles so that
|
|
29992
|
-
// all polygons have whole number side lengths if snapping to sides
|
|
29993
|
-
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
29994
|
-
|
|
29995
|
-
// Generate coords of a regular polygon with n sides
|
|
29996
|
-
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
29997
|
-
}
|
|
29998
|
-
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
29999
|
-
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
30000
|
-
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
30001
|
-
return coords;
|
|
30002
|
-
};
|
|
30003
|
-
const getSinusoidCoords = (graph, range, step) => {
|
|
30004
|
-
if (graph.coords) {
|
|
30005
|
-
return [graph.coords[0], graph.coords[1]];
|
|
30006
|
-
}
|
|
30007
|
-
let coords = [[0.5, 0.5], [0.65, 0.6]];
|
|
30008
|
-
coords = normalizePoints(range, step, coords, true);
|
|
30009
|
-
return coords;
|
|
30010
|
-
};
|
|
30011
|
-
const getQuadraticCoords = (graph, range, step) => {
|
|
30012
|
-
if (graph.coords) {
|
|
30013
|
-
return graph.coords;
|
|
30014
|
-
}
|
|
30015
|
-
const defaultCoords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
|
|
30016
|
-
return normalizePoints(range, step, defaultCoords, true);
|
|
30017
|
-
};
|
|
30018
|
-
const getCircleCoords = (graph, range, step) => {
|
|
30019
|
-
if (graph.center != null && graph.radius != null) {
|
|
30020
|
-
return {
|
|
30021
|
-
center: graph.center,
|
|
30022
|
-
radiusPoint: vec.add(graph.center, [graph.radius, 0])
|
|
30023
|
-
};
|
|
30024
|
-
}
|
|
30025
|
-
return {
|
|
30026
|
-
center: [0, 0],
|
|
30027
|
-
radiusPoint: [1, 0]
|
|
30028
|
-
};
|
|
30029
|
-
};
|
|
30030
29838
|
|
|
30031
29839
|
/**
|
|
30032
29840
|
* determine radius of a circle graph
|
|
@@ -30160,38 +29968,16 @@ function QuadraticGraph(props) {
|
|
|
30160
29968
|
|
|
30161
29969
|
// Calculate the y value based on the current x value
|
|
30162
29970
|
const y = x => (a * x + b) * x + c;
|
|
30163
|
-
const handleOnMove = (destination, elementId) => {
|
|
30164
|
-
// If the destination is invalid, we want do not want to move the point
|
|
30165
|
-
const validDestination = isValidDestination(destination, elementId, coords);
|
|
30166
|
-
if (validDestination === false) {
|
|
30167
|
-
return;
|
|
30168
|
-
}
|
|
30169
|
-
dispatch(movePoint(elementId, destination));
|
|
30170
|
-
};
|
|
30171
29971
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Plot$3.OfX, {
|
|
30172
29972
|
y: y,
|
|
30173
29973
|
color: color.blue
|
|
30174
29974
|
}), coords.map((coord, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
30175
29975
|
key: "point-" + i,
|
|
30176
29976
|
point: coord,
|
|
30177
|
-
onMove: destination =>
|
|
29977
|
+
onMove: destination => dispatch(movePoint(i, destination))
|
|
30178
29978
|
})));
|
|
30179
29979
|
}
|
|
30180
29980
|
|
|
30181
|
-
// Ensure that we are only snapping to coordinates that result in a valid quadratic equation
|
|
30182
|
-
const isValidDestination = (destination, elementId, coords) => {
|
|
30183
|
-
// Set up the new coords and check if the quadratic coefficients are valid
|
|
30184
|
-
const newCoords = [...coords];
|
|
30185
|
-
newCoords[elementId] = destination;
|
|
30186
|
-
const QuadraticCoefficients = getQuadraticCoefficients(newCoords);
|
|
30187
|
-
|
|
30188
|
-
// If the new destination results in an invalid quadratic equation, we don't want to move the point
|
|
30189
|
-
if (QuadraticCoefficients === undefined) {
|
|
30190
|
-
return false;
|
|
30191
|
-
}
|
|
30192
|
-
return true;
|
|
30193
|
-
};
|
|
30194
|
-
|
|
30195
29981
|
// Get the quadratic coefficients from the 3 control points
|
|
30196
29982
|
// These equations were originally set up in 2013 and may require some
|
|
30197
29983
|
// additional comments to help clarify the quadratic formula manipulations
|
|
@@ -31335,8 +31121,207 @@ const LegacyGrid = ({
|
|
|
31335
31121
|
return null;
|
|
31336
31122
|
};
|
|
31337
31123
|
|
|
31124
|
+
function initializeGraphState(params) {
|
|
31125
|
+
var _graph$snapTo;
|
|
31126
|
+
const {
|
|
31127
|
+
graph,
|
|
31128
|
+
step,
|
|
31129
|
+
snapStep,
|
|
31130
|
+
range
|
|
31131
|
+
} = params;
|
|
31132
|
+
const shared = {
|
|
31133
|
+
hasBeenInteractedWith: false,
|
|
31134
|
+
range,
|
|
31135
|
+
snapStep
|
|
31136
|
+
};
|
|
31137
|
+
switch (graph.type) {
|
|
31138
|
+
case "segment":
|
|
31139
|
+
return _extends({}, shared, {
|
|
31140
|
+
type: "segment",
|
|
31141
|
+
coords: getSegmentCoords(graph, range, step)
|
|
31142
|
+
});
|
|
31143
|
+
case "linear":
|
|
31144
|
+
return _extends({}, shared, {
|
|
31145
|
+
type: graph.type,
|
|
31146
|
+
coords: getLineCoords(graph, range, step)
|
|
31147
|
+
});
|
|
31148
|
+
case "ray":
|
|
31149
|
+
return _extends({}, shared, {
|
|
31150
|
+
type: graph.type,
|
|
31151
|
+
coords: getLineCoords(graph, range, step)
|
|
31152
|
+
});
|
|
31153
|
+
case "linear-system":
|
|
31154
|
+
return _extends({}, shared, {
|
|
31155
|
+
type: graph.type,
|
|
31156
|
+
coords: getLinearSystemCoords(graph, range, step)
|
|
31157
|
+
});
|
|
31158
|
+
case "polygon":
|
|
31159
|
+
return _extends({}, shared, {
|
|
31160
|
+
type: "polygon",
|
|
31161
|
+
showAngles: Boolean(graph.showAngles),
|
|
31162
|
+
showSides: Boolean(graph.showSides),
|
|
31163
|
+
coords: getPolygonCoords(graph, range, step),
|
|
31164
|
+
snapTo: (_graph$snapTo = graph.snapTo) != null ? _graph$snapTo : "grid"
|
|
31165
|
+
});
|
|
31166
|
+
case "point":
|
|
31167
|
+
return _extends({}, shared, {
|
|
31168
|
+
type: graph.type,
|
|
31169
|
+
coords: getPointCoords(graph, range, step)
|
|
31170
|
+
});
|
|
31171
|
+
case "circle":
|
|
31172
|
+
return _extends({}, shared, {
|
|
31173
|
+
type: graph.type
|
|
31174
|
+
}, getCircleCoords(graph));
|
|
31175
|
+
case "quadratic":
|
|
31176
|
+
return _extends({}, shared, {
|
|
31177
|
+
type: graph.type,
|
|
31178
|
+
coords: getQuadraticCoords(graph, range, step)
|
|
31179
|
+
});
|
|
31180
|
+
case "sinusoid":
|
|
31181
|
+
return _extends({}, shared, {
|
|
31182
|
+
type: graph.type,
|
|
31183
|
+
coords: getSinusoidCoords(graph, range, step)
|
|
31184
|
+
});
|
|
31185
|
+
case "angle":
|
|
31186
|
+
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
31187
|
+
default:
|
|
31188
|
+
throw new UnreachableCaseError(graph);
|
|
31189
|
+
}
|
|
31190
|
+
}
|
|
31191
|
+
function getPointCoords(graph, range, step) {
|
|
31192
|
+
var _graph$coords;
|
|
31193
|
+
const numPoints = graph.numPoints || 1;
|
|
31194
|
+
let coords = (_graph$coords = graph.coords) == null ? void 0 : _graph$coords.slice();
|
|
31195
|
+
if (coords) {
|
|
31196
|
+
return coords;
|
|
31197
|
+
}
|
|
31198
|
+
switch (numPoints) {
|
|
31199
|
+
case 1:
|
|
31200
|
+
// Back in the day, one point's coords were in graph.coord
|
|
31201
|
+
coords = [graph.coord || [0, 0]];
|
|
31202
|
+
break;
|
|
31203
|
+
case 2:
|
|
31204
|
+
coords = [[-5, 0], [5, 0]];
|
|
31205
|
+
break;
|
|
31206
|
+
case 3:
|
|
31207
|
+
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
31208
|
+
break;
|
|
31209
|
+
case 4:
|
|
31210
|
+
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
31211
|
+
break;
|
|
31212
|
+
case 5:
|
|
31213
|
+
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
31214
|
+
break;
|
|
31215
|
+
case 6:
|
|
31216
|
+
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
31217
|
+
break;
|
|
31218
|
+
default:
|
|
31219
|
+
coords = [];
|
|
31220
|
+
break;
|
|
31221
|
+
}
|
|
31222
|
+
// Transform coords from their -10 to 10 space to 0 to 1
|
|
31223
|
+
// because of the old graph.coord, and also it's easier.
|
|
31224
|
+
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
31225
|
+
return normalizePoints(range, step, newCoords);
|
|
31226
|
+
}
|
|
31227
|
+
function getSegmentCoords(graph, range, step) {
|
|
31228
|
+
if (graph.coords) {
|
|
31229
|
+
return graph.coords;
|
|
31230
|
+
}
|
|
31231
|
+
const ys = n => {
|
|
31232
|
+
switch (n) {
|
|
31233
|
+
case 2:
|
|
31234
|
+
return [5, -5];
|
|
31235
|
+
case 3:
|
|
31236
|
+
return [5, 0, -5];
|
|
31237
|
+
case 4:
|
|
31238
|
+
return [6, 2, -2, -6];
|
|
31239
|
+
case 5:
|
|
31240
|
+
return [6, 3, 0, -3, -6];
|
|
31241
|
+
case 6:
|
|
31242
|
+
return [5, 3, 1, -1, -3, -5];
|
|
31243
|
+
default:
|
|
31244
|
+
return [5];
|
|
31245
|
+
}
|
|
31246
|
+
};
|
|
31247
|
+
const defaultRange = [[-10, 10], [-10, 10]];
|
|
31248
|
+
return ys(graph.numSegments).map(y => {
|
|
31249
|
+
let endpoints = [[-5, y], [5, y]];
|
|
31250
|
+
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
31251
|
+
endpoints = normalizePoints(range, step, endpoints);
|
|
31252
|
+
return endpoints;
|
|
31253
|
+
});
|
|
31254
|
+
}
|
|
31255
|
+
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
31256
|
+
function getLineCoords(graph, range, step) {
|
|
31257
|
+
if (graph.coords) {
|
|
31258
|
+
return graph.coords;
|
|
31259
|
+
}
|
|
31260
|
+
return normalizePoints(range, step, defaultLinearCoords[0]);
|
|
31261
|
+
}
|
|
31262
|
+
function getLinearSystemCoords(graph, range, step) {
|
|
31263
|
+
if (graph.coords) {
|
|
31264
|
+
return graph.coords;
|
|
31265
|
+
}
|
|
31266
|
+
return defaultLinearCoords.map(points => normalizePoints(range, step, points));
|
|
31267
|
+
}
|
|
31268
|
+
function getPolygonCoords(graph, range, step) {
|
|
31269
|
+
var _graph$coords2;
|
|
31270
|
+
let coords = (_graph$coords2 = graph.coords) == null ? void 0 : _graph$coords2.slice();
|
|
31271
|
+
if (coords) {
|
|
31272
|
+
return coords;
|
|
31273
|
+
}
|
|
31274
|
+
const n = graph.numSides || 3;
|
|
31275
|
+
if (n === "unlimited") {
|
|
31276
|
+
coords = [];
|
|
31277
|
+
} else {
|
|
31278
|
+
const angle = 2 * Math.PI / n;
|
|
31279
|
+
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
31280
|
+
|
|
31281
|
+
// TODO(alex): Generalize this to more than just triangles so that
|
|
31282
|
+
// all polygons have whole number side lengths if snapping to sides
|
|
31283
|
+
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
31284
|
+
|
|
31285
|
+
// Generate coords of a regular polygon with n sides
|
|
31286
|
+
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
31287
|
+
}
|
|
31288
|
+
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
31289
|
+
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
31290
|
+
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
31291
|
+
return coords;
|
|
31292
|
+
}
|
|
31293
|
+
function getSinusoidCoords(graph, range, step) {
|
|
31294
|
+
if (graph.coords) {
|
|
31295
|
+
return [graph.coords[0], graph.coords[1]];
|
|
31296
|
+
}
|
|
31297
|
+
let coords = [[0.5, 0.5], [0.65, 0.6]];
|
|
31298
|
+
coords = normalizePoints(range, step, coords, true);
|
|
31299
|
+
return coords;
|
|
31300
|
+
}
|
|
31301
|
+
function getQuadraticCoords(graph, range, step) {
|
|
31302
|
+
if (graph.coords) {
|
|
31303
|
+
return graph.coords;
|
|
31304
|
+
}
|
|
31305
|
+
const defaultCoords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
|
|
31306
|
+
return normalizePoints(range, step, defaultCoords, true);
|
|
31307
|
+
}
|
|
31308
|
+
function getCircleCoords(graph) {
|
|
31309
|
+
if (graph.center != null && graph.radius != null) {
|
|
31310
|
+
return {
|
|
31311
|
+
center: graph.center,
|
|
31312
|
+
radiusPoint: vec.add(graph.center, [graph.radius, 0])
|
|
31313
|
+
};
|
|
31314
|
+
}
|
|
31315
|
+
return {
|
|
31316
|
+
center: [0, 0],
|
|
31317
|
+
radiusPoint: [2, 0]
|
|
31318
|
+
};
|
|
31319
|
+
}
|
|
31320
|
+
|
|
31338
31321
|
function interactiveGraphReducer(state, action) {
|
|
31339
31322
|
switch (action.type) {
|
|
31323
|
+
case REINITIALIZE:
|
|
31324
|
+
return initializeGraphState(action.params);
|
|
31340
31325
|
case MOVE_CONTROL_POINT:
|
|
31341
31326
|
return doMoveControlPoint(state, action);
|
|
31342
31327
|
case MOVE_LINE:
|
|
@@ -31468,11 +31453,20 @@ function doMoveAll(state, action) {
|
|
|
31468
31453
|
switch (state.type) {
|
|
31469
31454
|
case "polygon":
|
|
31470
31455
|
{
|
|
31471
|
-
|
|
31472
|
-
|
|
31473
|
-
|
|
31474
|
-
|
|
31475
|
-
|
|
31456
|
+
let newCoords;
|
|
31457
|
+
if (state.snapTo === "sides" || state.snapTo === "angles") {
|
|
31458
|
+
const change = getChange(state.coords, action.delta, {
|
|
31459
|
+
snapStep: [0, 0],
|
|
31460
|
+
range
|
|
31461
|
+
});
|
|
31462
|
+
newCoords = state.coords.map(point => vec.add(point, change));
|
|
31463
|
+
} else {
|
|
31464
|
+
const change = getChange(state.coords, action.delta, {
|
|
31465
|
+
snapStep,
|
|
31466
|
+
range
|
|
31467
|
+
});
|
|
31468
|
+
newCoords = state.coords.map(point => snap(snapStep, vec.add(point, change)));
|
|
31469
|
+
}
|
|
31476
31470
|
return _extends({}, state, {
|
|
31477
31471
|
hasBeenInteractedWith: true,
|
|
31478
31472
|
coords: newCoords
|
|
@@ -31486,10 +31480,18 @@ function doMoveAll(state, action) {
|
|
|
31486
31480
|
function doMovePoint(state, action) {
|
|
31487
31481
|
switch (state.type) {
|
|
31488
31482
|
case "polygon":
|
|
31483
|
+
let newValue;
|
|
31484
|
+
if (state.snapTo === "sides") {
|
|
31485
|
+
newValue = boundAndSnapToSides(action.destination, state, action.index);
|
|
31486
|
+
} else if (state.snapTo === "angles") {
|
|
31487
|
+
newValue = boundAndSnapToAngle(action.destination, state, action.index);
|
|
31488
|
+
} else {
|
|
31489
|
+
newValue = boundAndSnapToGrid(action.destination, state);
|
|
31490
|
+
}
|
|
31489
31491
|
const newCoords = setAtIndex({
|
|
31490
31492
|
array: state.coords,
|
|
31491
31493
|
index: action.index,
|
|
31492
|
-
newValue:
|
|
31494
|
+
newValue: newValue
|
|
31493
31495
|
});
|
|
31494
31496
|
|
|
31495
31497
|
// Reject the move if it would cause the sides of the polygon to cross
|
|
@@ -31532,6 +31534,15 @@ function doMovePoint(state, action) {
|
|
|
31532
31534
|
}
|
|
31533
31535
|
case "quadratic":
|
|
31534
31536
|
{
|
|
31537
|
+
// Set up the new coords and check if the quadratic coefficients are valid
|
|
31538
|
+
const _newCoords2 = [...state.coords];
|
|
31539
|
+
_newCoords2[action.index] = action.destination;
|
|
31540
|
+
const QuadraticCoefficients = getQuadraticCoefficients(_newCoords2);
|
|
31541
|
+
|
|
31542
|
+
// If the new destination results in an invalid quadratic equation, we don't want to move the point
|
|
31543
|
+
if (QuadraticCoefficients === undefined) {
|
|
31544
|
+
return state;
|
|
31545
|
+
}
|
|
31535
31546
|
return _extends({}, state, {
|
|
31536
31547
|
hasBeenInteractedWith: true,
|
|
31537
31548
|
coords: setAtIndex({
|
|
@@ -31624,14 +31635,8 @@ function doChangeRange(state, action) {
|
|
|
31624
31635
|
range: action.range
|
|
31625
31636
|
});
|
|
31626
31637
|
}
|
|
31627
|
-
const
|
|
31638
|
+
const getDeltaVertex = (maxMoves, minMoves, delta) => {
|
|
31628
31639
|
const [deltaX, deltaY] = delta;
|
|
31629
|
-
const maxMoves = coords.map(point => maxMove(_extends({}, constraintOpts, {
|
|
31630
|
-
point
|
|
31631
|
-
})));
|
|
31632
|
-
const minMoves = coords.map(point => minMove(_extends({}, constraintOpts, {
|
|
31633
|
-
point
|
|
31634
|
-
})));
|
|
31635
31640
|
const maxXMove = Math.min(...maxMoves.map(move => move[0]));
|
|
31636
31641
|
const maxYMove = Math.min(...maxMoves.map(move => move[1]));
|
|
31637
31642
|
const minXMove = Math.max(...minMoves.map(move => move[0]));
|
|
@@ -31640,6 +31645,22 @@ const getChange = (coords, delta, constraintOpts) => {
|
|
|
31640
31645
|
const dy = clamp(deltaY, minYMove, maxYMove);
|
|
31641
31646
|
return [dx, dy];
|
|
31642
31647
|
};
|
|
31648
|
+
const getChange = (coords, delta, constraintOpts) => {
|
|
31649
|
+
const maxMoves = coords.map(point => maxMove(_extends({}, constraintOpts, {
|
|
31650
|
+
point
|
|
31651
|
+
})));
|
|
31652
|
+
const minMoves = coords.map(point => minMove(_extends({}, constraintOpts, {
|
|
31653
|
+
point
|
|
31654
|
+
})));
|
|
31655
|
+
const [dx, dy] = getDeltaVertex(maxMoves, minMoves, delta);
|
|
31656
|
+
return [dx, dy];
|
|
31657
|
+
};
|
|
31658
|
+
const eq$1 = Util.eq;
|
|
31659
|
+
|
|
31660
|
+
// Less than or approximately equal
|
|
31661
|
+
function leq$1(a, b) {
|
|
31662
|
+
return a < b || eq$1(a, b);
|
|
31663
|
+
}
|
|
31643
31664
|
function boundAndSnapToGrid(point, {
|
|
31644
31665
|
snapStep,
|
|
31645
31666
|
range
|
|
@@ -31650,6 +31671,131 @@ function boundAndSnapToGrid(point, {
|
|
|
31650
31671
|
point
|
|
31651
31672
|
}));
|
|
31652
31673
|
}
|
|
31674
|
+
function boundAndSnapToAngle(destinationPoint, {
|
|
31675
|
+
range,
|
|
31676
|
+
coords
|
|
31677
|
+
}, index) {
|
|
31678
|
+
const startingPoint = coords[index];
|
|
31679
|
+
|
|
31680
|
+
// Needed to prevent updating the original coords before the checks for
|
|
31681
|
+
// degenerate triangles and overlapping sides
|
|
31682
|
+
const coordsCopy = [...coords];
|
|
31683
|
+
|
|
31684
|
+
// Takes the destination point and makes sure it is within the bounds of the graph
|
|
31685
|
+
// SnapStep is [0, 0] because we don't want to snap to the grid
|
|
31686
|
+
coordsCopy[index] = bound$1({
|
|
31687
|
+
snapStep: [0, 0],
|
|
31688
|
+
range,
|
|
31689
|
+
point: destinationPoint
|
|
31690
|
+
});
|
|
31691
|
+
|
|
31692
|
+
// Gets the radian angles between the coords and maps them to degrees
|
|
31693
|
+
const angles = angleMeasures(coordsCopy).map(angle => angle * 180 / Math.PI);
|
|
31694
|
+
|
|
31695
|
+
// Gets the relative index of a point
|
|
31696
|
+
const rel = j => {
|
|
31697
|
+
return (index + j + coordsCopy.length) % coordsCopy.length;
|
|
31698
|
+
};
|
|
31699
|
+
|
|
31700
|
+
// Round the angles to left and right of the current point
|
|
31701
|
+
_.each([-1, 1], function (j) {
|
|
31702
|
+
angles[rel(j)] = Math.round(angles[rel(j)]);
|
|
31703
|
+
});
|
|
31704
|
+
const getAngle = function getAngle(a, vertex, b) {
|
|
31705
|
+
const angle = GraphUtils.findAngle(coordsCopy[rel(a)], coordsCopy[rel(b)], coordsCopy[rel(vertex)]);
|
|
31706
|
+
return (angle + 360) % 360;
|
|
31707
|
+
};
|
|
31708
|
+
const innerAngles = [angles[rel(-1)] - getAngle(-2, -1, 1), angles[rel(1)] - getAngle(-1, 1, 2)];
|
|
31709
|
+
innerAngles[2] = 180 - (innerAngles[0] + innerAngles[1]);
|
|
31710
|
+
|
|
31711
|
+
// Avoid degenerate triangles
|
|
31712
|
+
if (innerAngles.some(function (angle) {
|
|
31713
|
+
return leq$1(angle, 1);
|
|
31714
|
+
})) {
|
|
31715
|
+
return startingPoint;
|
|
31716
|
+
}
|
|
31717
|
+
const knownSide = magnitude(
|
|
31718
|
+
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
31719
|
+
vector(coordsCopy[rel(-1)], coordsCopy[rel(1)]));
|
|
31720
|
+
|
|
31721
|
+
// Returns true if the points form a counter-clockwise turn;
|
|
31722
|
+
// a.k.a if the point is on the left or right of the polygon.
|
|
31723
|
+
// This is used to determine how to adjust the point: if the point is on the left,
|
|
31724
|
+
// we want to add the inner angle to the outer angle, and if it's on the right,
|
|
31725
|
+
// we want to subtract the inner angle from the outer angle. The angle solved
|
|
31726
|
+
// for is then used in the polar function to determine the new point.
|
|
31727
|
+
const onLeft = sign(ccw(coordsCopy[rel(-1)], coordsCopy[rel(1)], coordsCopy[index])) === 1;
|
|
31728
|
+
|
|
31729
|
+
// Solve for side by using the law of sines
|
|
31730
|
+
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
31731
|
+
|
|
31732
|
+
// Angle at the second vertex of the polygon
|
|
31733
|
+
const outerAngle = GraphUtils.findAngle(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
31734
|
+
|
|
31735
|
+
// Uses the length of the side of the polygon (radial coordinate)
|
|
31736
|
+
// and the angle between the first and second sides of the
|
|
31737
|
+
// polygon (angular coordinate) to determine how to adjust the point
|
|
31738
|
+
const offset = polar(side, outerAngle + (onLeft ? 1 : -1) * innerAngles[0]);
|
|
31739
|
+
return vector$1.add(coordsCopy[rel(-1)], offset);
|
|
31740
|
+
}
|
|
31741
|
+
function boundAndSnapToSides(destinationPoint, {
|
|
31742
|
+
range,
|
|
31743
|
+
coords
|
|
31744
|
+
}, index) {
|
|
31745
|
+
const startingPoint = coords[index];
|
|
31746
|
+
|
|
31747
|
+
// Needed to prevent updating the original coords before the checks for
|
|
31748
|
+
// degenerate triangles and overlapping sides
|
|
31749
|
+
const coordsCopy = [...coords];
|
|
31750
|
+
|
|
31751
|
+
// Takes the destination point and makes sure it is within the bounds of the graph
|
|
31752
|
+
// SnapStep is [0, 0] because we don't want to snap to the grid
|
|
31753
|
+
coordsCopy[index] = bound$1({
|
|
31754
|
+
snapStep: [0, 0],
|
|
31755
|
+
range,
|
|
31756
|
+
point: destinationPoint
|
|
31757
|
+
});
|
|
31758
|
+
|
|
31759
|
+
// Gets the relative index of a point
|
|
31760
|
+
const rel = j => {
|
|
31761
|
+
return (index + j + coordsCopy.length) % coordsCopy.length;
|
|
31762
|
+
};
|
|
31763
|
+
const sides = _.map([[coordsCopy[rel(-1)], coordsCopy[index]], [coordsCopy[index], coordsCopy[rel(1)]], [coordsCopy[rel(-1)], coordsCopy[rel(1)]]], function (coordsCopy) {
|
|
31764
|
+
// @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.
|
|
31765
|
+
return magnitude(vector(...coordsCopy));
|
|
31766
|
+
});
|
|
31767
|
+
|
|
31768
|
+
// Round the sides to left and right of the current point
|
|
31769
|
+
_.each([0, 1], function (j) {
|
|
31770
|
+
sides[j] = Math.round(sides[j]);
|
|
31771
|
+
});
|
|
31772
|
+
|
|
31773
|
+
// Avoid degenerate triangles
|
|
31774
|
+
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])) {
|
|
31775
|
+
return startingPoint;
|
|
31776
|
+
}
|
|
31777
|
+
|
|
31778
|
+
// Solve for angle by using the law of cosines
|
|
31779
|
+
// Angle at the first vertex of the polygon
|
|
31780
|
+
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
31781
|
+
|
|
31782
|
+
// Angle at the second vertex of the polygon
|
|
31783
|
+
const outerAngle = GraphUtils.findAngle(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
31784
|
+
|
|
31785
|
+
// Returns true if the points form a counter-clockwise turn;
|
|
31786
|
+
// a.k.a if the point is on the left or right of the polygon.
|
|
31787
|
+
// This is used to determine how to adjust the point: if the point is on the left,
|
|
31788
|
+
// we want to add the inner angle to the outer angle, and if it's on the right,
|
|
31789
|
+
// we want to subtract the inner angle from the outer angle. The angle solved
|
|
31790
|
+
// for is then used in the polar function to determine the new point.
|
|
31791
|
+
const onLeft = sign(ccw(coordsCopy[rel(-1)], coordsCopy[rel(1)], coordsCopy[index])) === 1;
|
|
31792
|
+
|
|
31793
|
+
// Uses the length of the first side of the polygon (radial coordinate)
|
|
31794
|
+
// and the angle between the first and second sides of the
|
|
31795
|
+
// polygon (angular coordinate) to determine how to adjust the point
|
|
31796
|
+
const offset = polar(sides[0], outerAngle + (onLeft ? 1 : -1) * innerAngle);
|
|
31797
|
+
return vector$1.add(coordsCopy[rel(-1)], offset);
|
|
31798
|
+
}
|
|
31653
31799
|
|
|
31654
31800
|
// Returns the closest point to the given `point` that is within the graph
|
|
31655
31801
|
// bounds given in `state`.
|
|
@@ -31664,7 +31810,7 @@ function bound$1({
|
|
|
31664
31810
|
return [clamp(requestedX, minX + snapX, maxX - snapX), clamp(requestedY, minY + snapY, maxY - snapY)];
|
|
31665
31811
|
}
|
|
31666
31812
|
|
|
31667
|
-
// Returns the vector from the given point to the top-right corner of the graph
|
|
31813
|
+
// Returns the vector from the given point to the top-right corner of the graph when snapped to the grid
|
|
31668
31814
|
function maxMove({
|
|
31669
31815
|
snapStep,
|
|
31670
31816
|
range,
|
|
@@ -31678,8 +31824,7 @@ function maxMove({
|
|
|
31678
31824
|
return vec.sub(topRight, point);
|
|
31679
31825
|
}
|
|
31680
31826
|
|
|
31681
|
-
// Returns the vector from the given point to the bottom-left corner of the
|
|
31682
|
-
// graph
|
|
31827
|
+
// Returns the vector from the given point to the bottom-left corner of the graph when snapped to the grid
|
|
31683
31828
|
function minMove({
|
|
31684
31829
|
snapStep,
|
|
31685
31830
|
range,
|
|
@@ -31801,11 +31946,12 @@ function mafsStateToInteractiveGraph(state) {
|
|
|
31801
31946
|
}
|
|
31802
31947
|
const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
31803
31948
|
const {
|
|
31804
|
-
onChange
|
|
31949
|
+
onChange,
|
|
31950
|
+
graph
|
|
31805
31951
|
} = props;
|
|
31806
31952
|
const [state, dispatch] = React.useReducer(interactiveGraphReducer, props, initializeGraphState);
|
|
31807
31953
|
useImperativeHandle(ref, () => ({
|
|
31808
|
-
getUserInput: () => getGradableGraph(state,
|
|
31954
|
+
getUserInput: () => getGradableGraph(state, graph)
|
|
31809
31955
|
}));
|
|
31810
31956
|
const prevState = useRef(state);
|
|
31811
31957
|
useEffect(() => {
|
|
@@ -31828,6 +31974,18 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
31828
31974
|
useEffect(() => {
|
|
31829
31975
|
dispatch(changeRange([[xMinRange, xMaxRange], [yMinRange, yMaxRange]]));
|
|
31830
31976
|
}, [dispatch, xMinRange, xMaxRange, yMinRange, yMaxRange]);
|
|
31977
|
+
const numSegments = graph.type === "segment" ? graph.numSegments : null;
|
|
31978
|
+
const originalPropsRef = useRef(props);
|
|
31979
|
+
const latestPropsRef = useLatestRef(props);
|
|
31980
|
+
useEffect(() => {
|
|
31981
|
+
// This conditional prevents the state from being "reinitialized" right
|
|
31982
|
+
// after the first render. This is an optimization, but also prevents
|
|
31983
|
+
// a bug where the graph would be marked "incorrect" during grading
|
|
31984
|
+
// even if the user never interacted with it.
|
|
31985
|
+
if (latestPropsRef.current !== originalPropsRef.current) {
|
|
31986
|
+
dispatch(reinitialize(latestPropsRef.current));
|
|
31987
|
+
}
|
|
31988
|
+
}, [graph.type, numSegments, latestPropsRef]);
|
|
31831
31989
|
return /*#__PURE__*/React.createElement(MafsGraph, _extends({}, props, {
|
|
31832
31990
|
state: state,
|
|
31833
31991
|
dispatch: dispatch
|
|
@@ -43994,7 +44152,7 @@ const init = function init(options) {
|
|
|
43994
44152
|
|
|
43995
44153
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
43996
44154
|
const libName = "@khanacademy/perseus";
|
|
43997
|
-
const libVersion = "23.
|
|
44155
|
+
const libVersion = "23.5.0";
|
|
43998
44156
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
43999
44157
|
|
|
44000
44158
|
const apiVersion = {
|
|
@@ -46995,5 +47153,5 @@ const EditorJsonify = {
|
|
|
46995
47153
|
}
|
|
46996
47154
|
};
|
|
46997
47155
|
|
|
46998
|
-
export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAnswerFromUserInput, getAnswersFromWidgets, getCorrectAnswerForWidgetId, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion,
|
|
47156
|
+
export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAnswerFromUserInput, getAnswersFromWidgets, getCorrectAnswerForWidgetId, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
46999
47157
|
//# sourceMappingURL=index.js.map
|