@khanacademy/perseus 23.3.0 → 23.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/index.css +8 -0
- package/dist/es/index.js +407 -300
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +8 -0
- package/dist/index.js +422 -319
- package/dist/index.js.map +1 -1
- package/dist/perseus-types.d.ts +1 -1
- 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 +1 -0
- package/dist/widgets/interactive-graphs/reducer/initialize-graph-state.d.ts +9 -0
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-state.d.ts +2 -9
- package/dist/widgets/interactive-graphs/types.d.ts +1 -0
- 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]);
|
|
@@ -29542,6 +29546,7 @@ function useControlPoint(point, color, onMovePoint) {
|
|
|
29542
29546
|
});
|
|
29543
29547
|
const focusableHandle = /*#__PURE__*/React__namespace.createElement("g", {
|
|
29544
29548
|
"data-testid": "movable-point__focusable-handle",
|
|
29549
|
+
className: "movable-point__focusable-handle",
|
|
29545
29550
|
tabIndex: 0,
|
|
29546
29551
|
ref: keyboardHandleRef,
|
|
29547
29552
|
onFocus: () => setFocused(true),
|
|
@@ -29711,7 +29716,6 @@ const LinearSystemGraph = props => {
|
|
|
29711
29716
|
const {
|
|
29712
29717
|
coords: lines
|
|
29713
29718
|
} = props.graphState;
|
|
29714
|
-
const colors = ["var(--movable-line-stroke-color)", "var(--mafs-violet)"];
|
|
29715
29719
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, lines?.map((line, i) => /*#__PURE__*/React__namespace.createElement(MovableLine, {
|
|
29716
29720
|
key: i,
|
|
29717
29721
|
points: line,
|
|
@@ -29723,7 +29727,7 @@ const LinearSystemGraph = props => {
|
|
|
29723
29727
|
end: true
|
|
29724
29728
|
},
|
|
29725
29729
|
onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i)),
|
|
29726
|
-
color:
|
|
29730
|
+
color: "var(--movable-line-stroke-color)"
|
|
29727
29731
|
})), ";");
|
|
29728
29732
|
};
|
|
29729
29733
|
|
|
@@ -29798,7 +29802,8 @@ const Angle = _ref => {
|
|
|
29798
29802
|
endPoints,
|
|
29799
29803
|
range,
|
|
29800
29804
|
polygonLines,
|
|
29801
|
-
showAngles
|
|
29805
|
+
showAngles,
|
|
29806
|
+
snapTo
|
|
29802
29807
|
} = _ref;
|
|
29803
29808
|
const [centerX, centerY] = centerPoint;
|
|
29804
29809
|
const areClockwise = clockwise$1([centerPoint, ...endPoints]);
|
|
@@ -29836,7 +29841,9 @@ const Angle = _ref => {
|
|
|
29836
29841
|
if (isOutside) {
|
|
29837
29842
|
angleInDegrees = 360 - angleInDegrees;
|
|
29838
29843
|
}
|
|
29839
|
-
|
|
29844
|
+
|
|
29845
|
+
// Only want to show whole numbers when in angles mode
|
|
29846
|
+
const angleLabelNumber = parseFloat(angleInDegrees.toFixed(snapTo === "angles" ? 0 : 1));
|
|
29840
29847
|
const angleLabel = Number.isInteger(angleLabelNumber) ? angleLabelNumber : "≈ " + angleLabelNumber;
|
|
29841
29848
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("defs", null, /*#__PURE__*/React__namespace.createElement("filter", {
|
|
29842
29849
|
id: "background",
|
|
@@ -29927,15 +29934,17 @@ const StyledMovablePoint = props => {
|
|
|
29927
29934
|
point,
|
|
29928
29935
|
onMove,
|
|
29929
29936
|
cursor,
|
|
29930
|
-
color = wonderBlocksTokens.color.blue
|
|
29937
|
+
color = wonderBlocksTokens.color.blue,
|
|
29938
|
+
snapTo
|
|
29931
29939
|
} = props;
|
|
29940
|
+
const snapToValue = snapTo ?? "grid";
|
|
29932
29941
|
const {
|
|
29933
29942
|
dragging
|
|
29934
29943
|
} = mafs.useMovable({
|
|
29935
29944
|
gestureTarget: elementRef,
|
|
29936
29945
|
point,
|
|
29937
29946
|
onMove,
|
|
29938
|
-
constrain: p => snap(snapStep, p)
|
|
29947
|
+
constrain: p => ["angles", "sides"].includes(snapToValue) ? p : snap(snapStep, p)
|
|
29939
29948
|
});
|
|
29940
29949
|
return /*#__PURE__*/React__namespace.createElement(MovablePointView, {
|
|
29941
29950
|
ref: elementRef,
|
|
@@ -29961,7 +29970,8 @@ const PolygonGraph = props => {
|
|
|
29961
29970
|
showAngles,
|
|
29962
29971
|
showSides,
|
|
29963
29972
|
range,
|
|
29964
|
-
snapStep
|
|
29973
|
+
snapStep,
|
|
29974
|
+
snapTo
|
|
29965
29975
|
} = props.graphState;
|
|
29966
29976
|
|
|
29967
29977
|
// TODO(benchristel): can the default set of points be removed here? I don't
|
|
@@ -29969,6 +29979,7 @@ const PolygonGraph = props => {
|
|
|
29969
29979
|
const points = coords ?? [[0, 0]];
|
|
29970
29980
|
const ref = React__namespace.useRef(null);
|
|
29971
29981
|
const dragReferencePoint = points[0];
|
|
29982
|
+
const snapToValue = snapTo ?? "grid";
|
|
29972
29983
|
const {
|
|
29973
29984
|
dragging
|
|
29974
29985
|
} = mafs.useMovable({
|
|
@@ -29978,7 +29989,7 @@ const PolygonGraph = props => {
|
|
|
29978
29989
|
const delta = mafs.vec.sub(newPoint, dragReferencePoint);
|
|
29979
29990
|
dispatch(moveAll(delta));
|
|
29980
29991
|
},
|
|
29981
|
-
constrain: p => snap(snapStep, p)
|
|
29992
|
+
constrain: p => ["angles", "sides"].includes(snapToValue) ? p : snap(snapStep, p)
|
|
29982
29993
|
});
|
|
29983
29994
|
const active = hovered || focused || dragging;
|
|
29984
29995
|
const lines = getLines(points);
|
|
@@ -30001,12 +30012,13 @@ const PolygonGraph = props => {
|
|
|
30001
30012
|
active: active,
|
|
30002
30013
|
range: range,
|
|
30003
30014
|
polygonLines: lines,
|
|
30004
|
-
showAngles: !!showAngles
|
|
30015
|
+
showAngles: !!showAngles,
|
|
30016
|
+
snapTo: snapToValue
|
|
30005
30017
|
});
|
|
30006
30018
|
}), showSides && lines.map((_ref, i) => {
|
|
30007
30019
|
let [start, end] = _ref;
|
|
30008
30020
|
const [x, y] = mafs.vec.midpoint(start, end);
|
|
30009
|
-
const length = parseFloat(mafs.vec.dist(start, end).toFixed(1));
|
|
30021
|
+
const length = parseFloat(mafs.vec.dist(start, end).toFixed(snapTo === "sides" ? 0 : 1));
|
|
30010
30022
|
return /*#__PURE__*/React__namespace.createElement(TextLabel, {
|
|
30011
30023
|
key: "side-" + i,
|
|
30012
30024
|
x: x,
|
|
@@ -30030,153 +30042,12 @@ const PolygonGraph = props => {
|
|
|
30030
30042
|
}
|
|
30031
30043
|
}), points.map((point, i) => /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
|
|
30032
30044
|
key: "point-" + i,
|
|
30045
|
+
snapTo: snapTo,
|
|
30033
30046
|
point: point,
|
|
30034
30047
|
onMove: destination => dispatch(movePoint(i, destination))
|
|
30035
30048
|
})));
|
|
30036
30049
|
};
|
|
30037
30050
|
|
|
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
30051
|
function getGradableGraph(state, initialGraph) {
|
|
30181
30052
|
if (!state.hasBeenInteractedWith) {
|
|
30182
30053
|
return {
|
|
@@ -30240,118 +30111,6 @@ function getGradableGraph(state, initialGraph) {
|
|
|
30240
30111
|
}
|
|
30241
30112
|
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
30242
30113
|
}
|
|
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
30114
|
|
|
30356
30115
|
/**
|
|
30357
30116
|
* determine radius of a circle graph
|
|
@@ -30485,38 +30244,16 @@ function QuadraticGraph(props) {
|
|
|
30485
30244
|
|
|
30486
30245
|
// Calculate the y value based on the current x value
|
|
30487
30246
|
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
30247
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(mafs.Plot.OfX, {
|
|
30497
30248
|
y: y,
|
|
30498
30249
|
color: wonderBlocksTokens.color.blue
|
|
30499
30250
|
}), coords.map((coord, i) => /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
|
|
30500
30251
|
key: "point-" + i,
|
|
30501
30252
|
point: coord,
|
|
30502
|
-
onMove: destination =>
|
|
30253
|
+
onMove: destination => dispatch(movePoint(i, destination))
|
|
30503
30254
|
})));
|
|
30504
30255
|
}
|
|
30505
30256
|
|
|
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
30257
|
// Get the quadratic coefficients from the 3 control points
|
|
30521
30258
|
// These equations were originally set up in 2013 and may require some
|
|
30522
30259
|
// additional comments to help clarify the quadratic formula manipulations
|
|
@@ -31666,6 +31403,210 @@ const LegacyGrid = _ref => {
|
|
|
31666
31403
|
return null;
|
|
31667
31404
|
};
|
|
31668
31405
|
|
|
31406
|
+
function initializeGraphState(params) {
|
|
31407
|
+
const {
|
|
31408
|
+
graph,
|
|
31409
|
+
step,
|
|
31410
|
+
snapStep,
|
|
31411
|
+
range
|
|
31412
|
+
} = params;
|
|
31413
|
+
const shared = {
|
|
31414
|
+
hasBeenInteractedWith: false,
|
|
31415
|
+
range,
|
|
31416
|
+
snapStep
|
|
31417
|
+
};
|
|
31418
|
+
switch (graph.type) {
|
|
31419
|
+
case "segment":
|
|
31420
|
+
return {
|
|
31421
|
+
...shared,
|
|
31422
|
+
type: "segment",
|
|
31423
|
+
coords: getSegmentCoords(graph, range, step)
|
|
31424
|
+
};
|
|
31425
|
+
case "linear":
|
|
31426
|
+
return {
|
|
31427
|
+
...shared,
|
|
31428
|
+
type: graph.type,
|
|
31429
|
+
coords: getLineCoords(graph, range, step)
|
|
31430
|
+
};
|
|
31431
|
+
case "ray":
|
|
31432
|
+
return {
|
|
31433
|
+
...shared,
|
|
31434
|
+
type: graph.type,
|
|
31435
|
+
coords: getLineCoords(graph, range, step)
|
|
31436
|
+
};
|
|
31437
|
+
case "linear-system":
|
|
31438
|
+
return {
|
|
31439
|
+
...shared,
|
|
31440
|
+
type: graph.type,
|
|
31441
|
+
coords: getLinearSystemCoords(graph, range, step)
|
|
31442
|
+
};
|
|
31443
|
+
case "polygon":
|
|
31444
|
+
return {
|
|
31445
|
+
...shared,
|
|
31446
|
+
type: "polygon",
|
|
31447
|
+
showAngles: Boolean(graph.showAngles),
|
|
31448
|
+
showSides: Boolean(graph.showSides),
|
|
31449
|
+
coords: getPolygonCoords(graph, range, step),
|
|
31450
|
+
snapTo: graph.snapTo ?? "grid"
|
|
31451
|
+
};
|
|
31452
|
+
case "point":
|
|
31453
|
+
return {
|
|
31454
|
+
...shared,
|
|
31455
|
+
type: graph.type,
|
|
31456
|
+
coords: getPointCoords(graph, range, step)
|
|
31457
|
+
};
|
|
31458
|
+
case "circle":
|
|
31459
|
+
return {
|
|
31460
|
+
...shared,
|
|
31461
|
+
type: graph.type,
|
|
31462
|
+
...getCircleCoords(graph)
|
|
31463
|
+
};
|
|
31464
|
+
case "quadratic":
|
|
31465
|
+
return {
|
|
31466
|
+
...shared,
|
|
31467
|
+
type: graph.type,
|
|
31468
|
+
coords: getQuadraticCoords(graph, range, step)
|
|
31469
|
+
};
|
|
31470
|
+
case "sinusoid":
|
|
31471
|
+
return {
|
|
31472
|
+
...shared,
|
|
31473
|
+
type: graph.type,
|
|
31474
|
+
coords: getSinusoidCoords(graph, range, step)
|
|
31475
|
+
};
|
|
31476
|
+
case "angle":
|
|
31477
|
+
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
31478
|
+
default:
|
|
31479
|
+
throw new wonderStuffCore.UnreachableCaseError(graph);
|
|
31480
|
+
}
|
|
31481
|
+
}
|
|
31482
|
+
function getPointCoords(graph, range, step) {
|
|
31483
|
+
const numPoints = graph.numPoints || 1;
|
|
31484
|
+
let coords = graph.coords?.slice();
|
|
31485
|
+
if (coords) {
|
|
31486
|
+
return coords;
|
|
31487
|
+
}
|
|
31488
|
+
switch (numPoints) {
|
|
31489
|
+
case 1:
|
|
31490
|
+
// Back in the day, one point's coords were in graph.coord
|
|
31491
|
+
coords = [graph.coord || [0, 0]];
|
|
31492
|
+
break;
|
|
31493
|
+
case 2:
|
|
31494
|
+
coords = [[-5, 0], [5, 0]];
|
|
31495
|
+
break;
|
|
31496
|
+
case 3:
|
|
31497
|
+
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
31498
|
+
break;
|
|
31499
|
+
case 4:
|
|
31500
|
+
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
31501
|
+
break;
|
|
31502
|
+
case 5:
|
|
31503
|
+
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
31504
|
+
break;
|
|
31505
|
+
case 6:
|
|
31506
|
+
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
31507
|
+
break;
|
|
31508
|
+
default:
|
|
31509
|
+
coords = [];
|
|
31510
|
+
break;
|
|
31511
|
+
}
|
|
31512
|
+
// Transform coords from their -10 to 10 space to 0 to 1
|
|
31513
|
+
// because of the old graph.coord, and also it's easier.
|
|
31514
|
+
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
31515
|
+
return normalizePoints(range, step, newCoords);
|
|
31516
|
+
}
|
|
31517
|
+
function getSegmentCoords(graph, range, step) {
|
|
31518
|
+
if (graph.coords) {
|
|
31519
|
+
return graph.coords;
|
|
31520
|
+
}
|
|
31521
|
+
const ys = n => {
|
|
31522
|
+
switch (n) {
|
|
31523
|
+
case 2:
|
|
31524
|
+
return [5, -5];
|
|
31525
|
+
case 3:
|
|
31526
|
+
return [5, 0, -5];
|
|
31527
|
+
case 4:
|
|
31528
|
+
return [6, 2, -2, -6];
|
|
31529
|
+
case 5:
|
|
31530
|
+
return [6, 3, 0, -3, -6];
|
|
31531
|
+
case 6:
|
|
31532
|
+
return [5, 3, 1, -1, -3, -5];
|
|
31533
|
+
default:
|
|
31534
|
+
return [5];
|
|
31535
|
+
}
|
|
31536
|
+
};
|
|
31537
|
+
const defaultRange = [[-10, 10], [-10, 10]];
|
|
31538
|
+
return ys(graph.numSegments).map(y => {
|
|
31539
|
+
let endpoints = [[-5, y], [5, y]];
|
|
31540
|
+
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
31541
|
+
endpoints = normalizePoints(range, step, endpoints);
|
|
31542
|
+
return endpoints;
|
|
31543
|
+
});
|
|
31544
|
+
}
|
|
31545
|
+
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
31546
|
+
function getLineCoords(graph, range, step) {
|
|
31547
|
+
if (graph.coords) {
|
|
31548
|
+
return graph.coords;
|
|
31549
|
+
}
|
|
31550
|
+
return normalizePoints(range, step, defaultLinearCoords[0]);
|
|
31551
|
+
}
|
|
31552
|
+
function getLinearSystemCoords(graph, range, step) {
|
|
31553
|
+
if (graph.coords) {
|
|
31554
|
+
return graph.coords;
|
|
31555
|
+
}
|
|
31556
|
+
return defaultLinearCoords.map(points => normalizePoints(range, step, points));
|
|
31557
|
+
}
|
|
31558
|
+
function getPolygonCoords(graph, range, step) {
|
|
31559
|
+
let coords = graph.coords?.slice();
|
|
31560
|
+
if (coords) {
|
|
31561
|
+
return coords;
|
|
31562
|
+
}
|
|
31563
|
+
const n = graph.numSides || 3;
|
|
31564
|
+
if (n === "unlimited") {
|
|
31565
|
+
coords = [];
|
|
31566
|
+
} else {
|
|
31567
|
+
const angle = 2 * Math.PI / n;
|
|
31568
|
+
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
31569
|
+
|
|
31570
|
+
// TODO(alex): Generalize this to more than just triangles so that
|
|
31571
|
+
// all polygons have whole number side lengths if snapping to sides
|
|
31572
|
+
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
31573
|
+
|
|
31574
|
+
// Generate coords of a regular polygon with n sides
|
|
31575
|
+
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
31576
|
+
}
|
|
31577
|
+
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
31578
|
+
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
31579
|
+
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
31580
|
+
return coords;
|
|
31581
|
+
}
|
|
31582
|
+
function getSinusoidCoords(graph, range, step) {
|
|
31583
|
+
if (graph.coords) {
|
|
31584
|
+
return [graph.coords[0], graph.coords[1]];
|
|
31585
|
+
}
|
|
31586
|
+
let coords = [[0.5, 0.5], [0.65, 0.6]];
|
|
31587
|
+
coords = normalizePoints(range, step, coords, true);
|
|
31588
|
+
return coords;
|
|
31589
|
+
}
|
|
31590
|
+
function getQuadraticCoords(graph, range, step) {
|
|
31591
|
+
if (graph.coords) {
|
|
31592
|
+
return graph.coords;
|
|
31593
|
+
}
|
|
31594
|
+
const defaultCoords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
|
|
31595
|
+
return normalizePoints(range, step, defaultCoords, true);
|
|
31596
|
+
}
|
|
31597
|
+
function getCircleCoords(graph) {
|
|
31598
|
+
if (graph.center != null && graph.radius != null) {
|
|
31599
|
+
return {
|
|
31600
|
+
center: graph.center,
|
|
31601
|
+
radiusPoint: mafs.vec.add(graph.center, [graph.radius, 0])
|
|
31602
|
+
};
|
|
31603
|
+
}
|
|
31604
|
+
return {
|
|
31605
|
+
center: [0, 0],
|
|
31606
|
+
radiusPoint: [2, 0]
|
|
31607
|
+
};
|
|
31608
|
+
}
|
|
31609
|
+
|
|
31669
31610
|
function interactiveGraphReducer(state, action) {
|
|
31670
31611
|
switch (action.type) {
|
|
31671
31612
|
case MOVE_CONTROL_POINT:
|
|
@@ -31803,11 +31744,20 @@ function doMoveAll(state, action) {
|
|
|
31803
31744
|
switch (state.type) {
|
|
31804
31745
|
case "polygon":
|
|
31805
31746
|
{
|
|
31806
|
-
|
|
31807
|
-
|
|
31808
|
-
|
|
31809
|
-
|
|
31810
|
-
|
|
31747
|
+
let newCoords;
|
|
31748
|
+
if (state.snapTo === "sides" || state.snapTo === "angles") {
|
|
31749
|
+
const change = getChange(state.coords, action.delta, {
|
|
31750
|
+
snapStep: [0, 0],
|
|
31751
|
+
range
|
|
31752
|
+
});
|
|
31753
|
+
newCoords = state.coords.map(point => mafs.vec.add(point, change));
|
|
31754
|
+
} else {
|
|
31755
|
+
const change = getChange(state.coords, action.delta, {
|
|
31756
|
+
snapStep,
|
|
31757
|
+
range
|
|
31758
|
+
});
|
|
31759
|
+
newCoords = state.coords.map(point => snap(snapStep, mafs.vec.add(point, change)));
|
|
31760
|
+
}
|
|
31811
31761
|
return {
|
|
31812
31762
|
...state,
|
|
31813
31763
|
hasBeenInteractedWith: true,
|
|
@@ -31822,10 +31772,18 @@ function doMoveAll(state, action) {
|
|
|
31822
31772
|
function doMovePoint(state, action) {
|
|
31823
31773
|
switch (state.type) {
|
|
31824
31774
|
case "polygon":
|
|
31775
|
+
let newValue;
|
|
31776
|
+
if (state.snapTo === "sides") {
|
|
31777
|
+
newValue = boundAndSnapToSides(action.destination, state, action.index);
|
|
31778
|
+
} else if (state.snapTo === "angles") {
|
|
31779
|
+
newValue = boundAndSnapToAngle(action.destination, state, action.index);
|
|
31780
|
+
} else {
|
|
31781
|
+
newValue = boundAndSnapToGrid(action.destination, state);
|
|
31782
|
+
}
|
|
31825
31783
|
const newCoords = setAtIndex({
|
|
31826
31784
|
array: state.coords,
|
|
31827
31785
|
index: action.index,
|
|
31828
|
-
newValue:
|
|
31786
|
+
newValue: newValue
|
|
31829
31787
|
});
|
|
31830
31788
|
|
|
31831
31789
|
// Reject the move if it would cause the sides of the polygon to cross
|
|
@@ -31871,6 +31829,15 @@ function doMovePoint(state, action) {
|
|
|
31871
31829
|
}
|
|
31872
31830
|
case "quadratic":
|
|
31873
31831
|
{
|
|
31832
|
+
// Set up the new coords and check if the quadratic coefficients are valid
|
|
31833
|
+
const newCoords = [...state.coords];
|
|
31834
|
+
newCoords[action.index] = action.destination;
|
|
31835
|
+
const QuadraticCoefficients = getQuadraticCoefficients(newCoords);
|
|
31836
|
+
|
|
31837
|
+
// If the new destination results in an invalid quadratic equation, we don't want to move the point
|
|
31838
|
+
if (QuadraticCoefficients === undefined) {
|
|
31839
|
+
return state;
|
|
31840
|
+
}
|
|
31874
31841
|
return {
|
|
31875
31842
|
...state,
|
|
31876
31843
|
hasBeenInteractedWith: true,
|
|
@@ -31968,8 +31935,17 @@ function doChangeRange(state, action) {
|
|
|
31968
31935
|
range: action.range
|
|
31969
31936
|
};
|
|
31970
31937
|
}
|
|
31971
|
-
const
|
|
31938
|
+
const getDeltaVertex = (maxMoves, minMoves, delta) => {
|
|
31972
31939
|
const [deltaX, deltaY] = delta;
|
|
31940
|
+
const maxXMove = Math.min(...maxMoves.map(move => move[0]));
|
|
31941
|
+
const maxYMove = Math.min(...maxMoves.map(move => move[1]));
|
|
31942
|
+
const minXMove = Math.max(...minMoves.map(move => move[0]));
|
|
31943
|
+
const minYMove = Math.max(...minMoves.map(move => move[1]));
|
|
31944
|
+
const dx = clamp(deltaX, minXMove, maxXMove);
|
|
31945
|
+
const dy = clamp(deltaY, minYMove, maxYMove);
|
|
31946
|
+
return [dx, dy];
|
|
31947
|
+
};
|
|
31948
|
+
const getChange = (coords, delta, constraintOpts) => {
|
|
31973
31949
|
const maxMoves = coords.map(point => maxMove({
|
|
31974
31950
|
...constraintOpts,
|
|
31975
31951
|
point
|
|
@@ -31978,14 +31954,15 @@ const getChange = (coords, delta, constraintOpts) => {
|
|
|
31978
31954
|
...constraintOpts,
|
|
31979
31955
|
point
|
|
31980
31956
|
}));
|
|
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);
|
|
31957
|
+
const [dx, dy] = getDeltaVertex(maxMoves, minMoves, delta);
|
|
31987
31958
|
return [dx, dy];
|
|
31988
31959
|
};
|
|
31960
|
+
const eq$1 = Util.eq;
|
|
31961
|
+
|
|
31962
|
+
// Less than or approximately equal
|
|
31963
|
+
function leq$1(a, b) {
|
|
31964
|
+
return a < b || eq$1(a, b);
|
|
31965
|
+
}
|
|
31989
31966
|
function boundAndSnapToGrid(point, _ref) {
|
|
31990
31967
|
let {
|
|
31991
31968
|
snapStep,
|
|
@@ -31997,28 +31974,155 @@ function boundAndSnapToGrid(point, _ref) {
|
|
|
31997
31974
|
point
|
|
31998
31975
|
}));
|
|
31999
31976
|
}
|
|
31977
|
+
function boundAndSnapToAngle(destinationPoint, _ref2, index) {
|
|
31978
|
+
let {
|
|
31979
|
+
range,
|
|
31980
|
+
coords
|
|
31981
|
+
} = _ref2;
|
|
31982
|
+
const startingPoint = coords[index];
|
|
31983
|
+
|
|
31984
|
+
// Needed to prevent updating the original coords before the checks for
|
|
31985
|
+
// degenerate triangles and overlapping sides
|
|
31986
|
+
const coordsCopy = [...coords];
|
|
31987
|
+
|
|
31988
|
+
// Takes the destination point and makes sure it is within the bounds of the graph
|
|
31989
|
+
// SnapStep is [0, 0] because we don't want to snap to the grid
|
|
31990
|
+
coordsCopy[index] = bound$1({
|
|
31991
|
+
snapStep: [0, 0],
|
|
31992
|
+
range,
|
|
31993
|
+
point: destinationPoint
|
|
31994
|
+
});
|
|
31995
|
+
|
|
31996
|
+
// Gets the radian angles between the coords and maps them to degrees
|
|
31997
|
+
const angles = angleMeasures(coordsCopy).map(angle => angle * 180 / Math.PI);
|
|
31998
|
+
|
|
31999
|
+
// Gets the relative index of a point
|
|
32000
|
+
const rel = j => {
|
|
32001
|
+
return (index + j + coordsCopy.length) % coordsCopy.length;
|
|
32002
|
+
};
|
|
32003
|
+
|
|
32004
|
+
// Round the angles to left and right of the current point
|
|
32005
|
+
___default["default"].each([-1, 1], function (j) {
|
|
32006
|
+
angles[rel(j)] = Math.round(angles[rel(j)]);
|
|
32007
|
+
});
|
|
32008
|
+
const getAngle = function (a, vertex, b) {
|
|
32009
|
+
const angle = GraphUtils.findAngle(coordsCopy[rel(a)], coordsCopy[rel(b)], coordsCopy[rel(vertex)]);
|
|
32010
|
+
return (angle + 360) % 360;
|
|
32011
|
+
};
|
|
32012
|
+
const innerAngles = [angles[rel(-1)] - getAngle(-2, -1, 1), angles[rel(1)] - getAngle(-1, 1, 2)];
|
|
32013
|
+
innerAngles[2] = 180 - (innerAngles[0] + innerAngles[1]);
|
|
32014
|
+
|
|
32015
|
+
// Avoid degenerate triangles
|
|
32016
|
+
if (innerAngles.some(function (angle) {
|
|
32017
|
+
return leq$1(angle, 1);
|
|
32018
|
+
})) {
|
|
32019
|
+
return startingPoint;
|
|
32020
|
+
}
|
|
32021
|
+
const knownSide = magnitude(
|
|
32022
|
+
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
32023
|
+
vector(coordsCopy[rel(-1)], coordsCopy[rel(1)]));
|
|
32024
|
+
|
|
32025
|
+
// Returns true if the points form a counter-clockwise turn;
|
|
32026
|
+
// a.k.a if the point is on the left or right of the polygon.
|
|
32027
|
+
// This is used to determine how to adjust the point: if the point is on the left,
|
|
32028
|
+
// we want to add the inner angle to the outer angle, and if it's on the right,
|
|
32029
|
+
// we want to subtract the inner angle from the outer angle. The angle solved
|
|
32030
|
+
// for is then used in the polar function to determine the new point.
|
|
32031
|
+
const onLeft = sign(ccw(coordsCopy[rel(-1)], coordsCopy[rel(1)], coordsCopy[index])) === 1;
|
|
32032
|
+
|
|
32033
|
+
// Solve for side by using the law of sines
|
|
32034
|
+
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
32035
|
+
|
|
32036
|
+
// Angle at the second vertex of the polygon
|
|
32037
|
+
const outerAngle = GraphUtils.findAngle(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
32038
|
+
|
|
32039
|
+
// Uses the length of the side of the polygon (radial coordinate)
|
|
32040
|
+
// and the angle between the first and second sides of the
|
|
32041
|
+
// polygon (angular coordinate) to determine how to adjust the point
|
|
32042
|
+
const offset = polar(side, outerAngle + (onLeft ? 1 : -1) * innerAngles[0]);
|
|
32043
|
+
return kmath.vector.add(coordsCopy[rel(-1)], offset);
|
|
32044
|
+
}
|
|
32045
|
+
function boundAndSnapToSides(destinationPoint, _ref3, index) {
|
|
32046
|
+
let {
|
|
32047
|
+
range,
|
|
32048
|
+
coords
|
|
32049
|
+
} = _ref3;
|
|
32050
|
+
const startingPoint = coords[index];
|
|
32051
|
+
|
|
32052
|
+
// Needed to prevent updating the original coords before the checks for
|
|
32053
|
+
// degenerate triangles and overlapping sides
|
|
32054
|
+
const coordsCopy = [...coords];
|
|
32055
|
+
|
|
32056
|
+
// Takes the destination point and makes sure it is within the bounds of the graph
|
|
32057
|
+
// SnapStep is [0, 0] because we don't want to snap to the grid
|
|
32058
|
+
coordsCopy[index] = bound$1({
|
|
32059
|
+
snapStep: [0, 0],
|
|
32060
|
+
range,
|
|
32061
|
+
point: destinationPoint
|
|
32062
|
+
});
|
|
32063
|
+
|
|
32064
|
+
// Gets the relative index of a point
|
|
32065
|
+
const rel = j => {
|
|
32066
|
+
return (index + j + coordsCopy.length) % coordsCopy.length;
|
|
32067
|
+
};
|
|
32068
|
+
const sides = ___default["default"].map([[coordsCopy[rel(-1)], coordsCopy[index]], [coordsCopy[index], coordsCopy[rel(1)]], [coordsCopy[rel(-1)], coordsCopy[rel(1)]]], function (coordsCopy) {
|
|
32069
|
+
// @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.
|
|
32070
|
+
return magnitude(vector(...coordsCopy));
|
|
32071
|
+
});
|
|
32072
|
+
|
|
32073
|
+
// Round the sides to left and right of the current point
|
|
32074
|
+
___default["default"].each([0, 1], function (j) {
|
|
32075
|
+
sides[j] = Math.round(sides[j]);
|
|
32076
|
+
});
|
|
32077
|
+
|
|
32078
|
+
// Avoid degenerate triangles
|
|
32079
|
+
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])) {
|
|
32080
|
+
return startingPoint;
|
|
32081
|
+
}
|
|
32082
|
+
|
|
32083
|
+
// Solve for angle by using the law of cosines
|
|
32084
|
+
// Angle at the first vertex of the polygon
|
|
32085
|
+
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
32086
|
+
|
|
32087
|
+
// Angle at the second vertex of the polygon
|
|
32088
|
+
const outerAngle = GraphUtils.findAngle(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
32089
|
+
|
|
32090
|
+
// Returns true if the points form a counter-clockwise turn;
|
|
32091
|
+
// a.k.a if the point is on the left or right of the polygon.
|
|
32092
|
+
// This is used to determine how to adjust the point: if the point is on the left,
|
|
32093
|
+
// we want to add the inner angle to the outer angle, and if it's on the right,
|
|
32094
|
+
// we want to subtract the inner angle from the outer angle. The angle solved
|
|
32095
|
+
// for is then used in the polar function to determine the new point.
|
|
32096
|
+
const onLeft = sign(ccw(coordsCopy[rel(-1)], coordsCopy[rel(1)], coordsCopy[index])) === 1;
|
|
32097
|
+
|
|
32098
|
+
// Uses the length of the first side of the polygon (radial coordinate)
|
|
32099
|
+
// and the angle between the first and second sides of the
|
|
32100
|
+
// polygon (angular coordinate) to determine how to adjust the point
|
|
32101
|
+
const offset = polar(sides[0], outerAngle + (onLeft ? 1 : -1) * innerAngle);
|
|
32102
|
+
return kmath.vector.add(coordsCopy[rel(-1)], offset);
|
|
32103
|
+
}
|
|
32000
32104
|
|
|
32001
32105
|
// Returns the closest point to the given `point` that is within the graph
|
|
32002
32106
|
// bounds given in `state`.
|
|
32003
|
-
function bound$1(
|
|
32107
|
+
function bound$1(_ref4) {
|
|
32004
32108
|
let {
|
|
32005
32109
|
snapStep,
|
|
32006
32110
|
range,
|
|
32007
32111
|
point
|
|
32008
|
-
} =
|
|
32112
|
+
} = _ref4;
|
|
32009
32113
|
const [requestedX, requestedY] = point;
|
|
32010
32114
|
const [snapX, snapY] = snapStep;
|
|
32011
32115
|
const [[minX, maxX], [minY, maxY]] = range;
|
|
32012
32116
|
return [clamp(requestedX, minX + snapX, maxX - snapX), clamp(requestedY, minY + snapY, maxY - snapY)];
|
|
32013
32117
|
}
|
|
32014
32118
|
|
|
32015
|
-
// Returns the vector from the given point to the top-right corner of the graph
|
|
32016
|
-
function maxMove(
|
|
32119
|
+
// Returns the vector from the given point to the top-right corner of the graph when snapped to the grid
|
|
32120
|
+
function maxMove(_ref5) {
|
|
32017
32121
|
let {
|
|
32018
32122
|
snapStep,
|
|
32019
32123
|
range,
|
|
32020
32124
|
point
|
|
32021
|
-
} =
|
|
32125
|
+
} = _ref5;
|
|
32022
32126
|
const topRight = bound$1({
|
|
32023
32127
|
snapStep,
|
|
32024
32128
|
range,
|
|
@@ -32027,14 +32131,13 @@ function maxMove(_ref3) {
|
|
|
32027
32131
|
return mafs.vec.sub(topRight, point);
|
|
32028
32132
|
}
|
|
32029
32133
|
|
|
32030
|
-
// Returns the vector from the given point to the bottom-left corner of the
|
|
32031
|
-
|
|
32032
|
-
function minMove(_ref4) {
|
|
32134
|
+
// Returns the vector from the given point to the bottom-left corner of the graph when snapped to the grid
|
|
32135
|
+
function minMove(_ref6) {
|
|
32033
32136
|
let {
|
|
32034
32137
|
snapStep,
|
|
32035
32138
|
range,
|
|
32036
32139
|
point
|
|
32037
|
-
} =
|
|
32140
|
+
} = _ref6;
|
|
32038
32141
|
const bottomLeft = bound$1({
|
|
32039
32142
|
snapStep,
|
|
32040
32143
|
range,
|
|
@@ -44343,7 +44446,7 @@ const init = function (options) {
|
|
|
44343
44446
|
|
|
44344
44447
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44345
44448
|
const libName = "@khanacademy/perseus";
|
|
44346
|
-
const libVersion = "23.
|
|
44449
|
+
const libVersion = "23.4.0";
|
|
44347
44450
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44348
44451
|
|
|
44349
44452
|
const apiVersion = {
|