@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/es/index.js
CHANGED
|
@@ -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]);
|
|
@@ -29250,6 +29254,7 @@ function useControlPoint(point, color, onMovePoint) {
|
|
|
29250
29254
|
});
|
|
29251
29255
|
const focusableHandle = /*#__PURE__*/React.createElement("g", {
|
|
29252
29256
|
"data-testid": "movable-point__focusable-handle",
|
|
29257
|
+
className: "movable-point__focusable-handle",
|
|
29253
29258
|
tabIndex: 0,
|
|
29254
29259
|
ref: keyboardHandleRef,
|
|
29255
29260
|
onFocus: () => setFocused(true),
|
|
@@ -29419,7 +29424,6 @@ const LinearSystemGraph = props => {
|
|
|
29419
29424
|
const {
|
|
29420
29425
|
coords: lines
|
|
29421
29426
|
} = props.graphState;
|
|
29422
|
-
const colors = ["var(--movable-line-stroke-color)", "var(--mafs-violet)"];
|
|
29423
29427
|
return /*#__PURE__*/React.createElement(React.Fragment, null, lines == null ? void 0 : lines.map((line, i) => /*#__PURE__*/React.createElement(MovableLine, {
|
|
29424
29428
|
key: i,
|
|
29425
29429
|
points: line,
|
|
@@ -29431,7 +29435,7 @@ const LinearSystemGraph = props => {
|
|
|
29431
29435
|
end: true
|
|
29432
29436
|
},
|
|
29433
29437
|
onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i)),
|
|
29434
|
-
color:
|
|
29438
|
+
color: "var(--movable-line-stroke-color)"
|
|
29435
29439
|
})), ";");
|
|
29436
29440
|
};
|
|
29437
29441
|
|
|
@@ -29503,7 +29507,8 @@ const Angle = ({
|
|
|
29503
29507
|
endPoints,
|
|
29504
29508
|
range,
|
|
29505
29509
|
polygonLines,
|
|
29506
|
-
showAngles
|
|
29510
|
+
showAngles,
|
|
29511
|
+
snapTo
|
|
29507
29512
|
}) => {
|
|
29508
29513
|
const [centerX, centerY] = centerPoint;
|
|
29509
29514
|
const areClockwise = clockwise$1([centerPoint, ...endPoints]);
|
|
@@ -29541,7 +29546,9 @@ const Angle = ({
|
|
|
29541
29546
|
if (isOutside) {
|
|
29542
29547
|
angleInDegrees = 360 - angleInDegrees;
|
|
29543
29548
|
}
|
|
29544
|
-
|
|
29549
|
+
|
|
29550
|
+
// Only want to show whole numbers when in angles mode
|
|
29551
|
+
const angleLabelNumber = parseFloat(angleInDegrees.toFixed(snapTo === "angles" ? 0 : 1));
|
|
29545
29552
|
const angleLabel = Number.isInteger(angleLabelNumber) ? angleLabelNumber : "≈ " + angleLabelNumber;
|
|
29546
29553
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("filter", {
|
|
29547
29554
|
id: "background",
|
|
@@ -29627,15 +29634,17 @@ const StyledMovablePoint = props => {
|
|
|
29627
29634
|
point,
|
|
29628
29635
|
onMove,
|
|
29629
29636
|
cursor,
|
|
29630
|
-
color: color$1 = color.blue
|
|
29637
|
+
color: color$1 = color.blue,
|
|
29638
|
+
snapTo
|
|
29631
29639
|
} = props;
|
|
29640
|
+
const snapToValue = snapTo != null ? snapTo : "grid";
|
|
29632
29641
|
const {
|
|
29633
29642
|
dragging
|
|
29634
29643
|
} = useMovable({
|
|
29635
29644
|
gestureTarget: elementRef,
|
|
29636
29645
|
point,
|
|
29637
29646
|
onMove,
|
|
29638
|
-
constrain: p => snap(snapStep, p)
|
|
29647
|
+
constrain: p => ["angles", "sides"].includes(snapToValue) ? p : snap(snapStep, p)
|
|
29639
29648
|
});
|
|
29640
29649
|
return /*#__PURE__*/React.createElement(MovablePointView, {
|
|
29641
29650
|
ref: elementRef,
|
|
@@ -29661,7 +29670,8 @@ const PolygonGraph = props => {
|
|
|
29661
29670
|
showAngles,
|
|
29662
29671
|
showSides,
|
|
29663
29672
|
range,
|
|
29664
|
-
snapStep
|
|
29673
|
+
snapStep,
|
|
29674
|
+
snapTo
|
|
29665
29675
|
} = props.graphState;
|
|
29666
29676
|
|
|
29667
29677
|
// TODO(benchristel): can the default set of points be removed here? I don't
|
|
@@ -29669,6 +29679,7 @@ const PolygonGraph = props => {
|
|
|
29669
29679
|
const points = coords != null ? coords : [[0, 0]];
|
|
29670
29680
|
const ref = React.useRef(null);
|
|
29671
29681
|
const dragReferencePoint = points[0];
|
|
29682
|
+
const snapToValue = snapTo != null ? snapTo : "grid";
|
|
29672
29683
|
const {
|
|
29673
29684
|
dragging
|
|
29674
29685
|
} = useMovable({
|
|
@@ -29678,7 +29689,7 @@ const PolygonGraph = props => {
|
|
|
29678
29689
|
const delta = vec.sub(newPoint, dragReferencePoint);
|
|
29679
29690
|
dispatch(moveAll(delta));
|
|
29680
29691
|
},
|
|
29681
|
-
constrain: p => snap(snapStep, p)
|
|
29692
|
+
constrain: p => ["angles", "sides"].includes(snapToValue) ? p : snap(snapStep, p)
|
|
29682
29693
|
});
|
|
29683
29694
|
const active = hovered || focused || dragging;
|
|
29684
29695
|
const lines = getLines(points);
|
|
@@ -29701,11 +29712,12 @@ const PolygonGraph = props => {
|
|
|
29701
29712
|
active: active,
|
|
29702
29713
|
range: range,
|
|
29703
29714
|
polygonLines: lines,
|
|
29704
|
-
showAngles: !!showAngles
|
|
29715
|
+
showAngles: !!showAngles,
|
|
29716
|
+
snapTo: snapToValue
|
|
29705
29717
|
});
|
|
29706
29718
|
}), showSides && lines.map(([start, end], i) => {
|
|
29707
29719
|
const [x, y] = vec.midpoint(start, end);
|
|
29708
|
-
const length = parseFloat(vec.dist(start, end).toFixed(1));
|
|
29720
|
+
const length = parseFloat(vec.dist(start, end).toFixed(snapTo === "sides" ? 0 : 1));
|
|
29709
29721
|
return /*#__PURE__*/React.createElement(TextLabel, {
|
|
29710
29722
|
key: "side-" + i,
|
|
29711
29723
|
x: x,
|
|
@@ -29729,143 +29741,12 @@ const PolygonGraph = props => {
|
|
|
29729
29741
|
}
|
|
29730
29742
|
}), points.map((point, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
29731
29743
|
key: "point-" + i,
|
|
29744
|
+
snapTo: snapTo,
|
|
29732
29745
|
point: point,
|
|
29733
29746
|
onMove: destination => dispatch(movePoint(i, destination))
|
|
29734
29747
|
})));
|
|
29735
29748
|
};
|
|
29736
29749
|
|
|
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
29750
|
function getGradableGraph(state, initialGraph) {
|
|
29870
29751
|
if (!state.hasBeenInteractedWith) {
|
|
29871
29752
|
return _extends({}, initialGraph);
|
|
@@ -29918,115 +29799,6 @@ function getGradableGraph(state, initialGraph) {
|
|
|
29918
29799
|
}
|
|
29919
29800
|
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
29920
29801
|
}
|
|
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
29802
|
|
|
30031
29803
|
/**
|
|
30032
29804
|
* determine radius of a circle graph
|
|
@@ -30160,38 +29932,16 @@ function QuadraticGraph(props) {
|
|
|
30160
29932
|
|
|
30161
29933
|
// Calculate the y value based on the current x value
|
|
30162
29934
|
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
29935
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Plot$3.OfX, {
|
|
30172
29936
|
y: y,
|
|
30173
29937
|
color: color.blue
|
|
30174
29938
|
}), coords.map((coord, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
30175
29939
|
key: "point-" + i,
|
|
30176
29940
|
point: coord,
|
|
30177
|
-
onMove: destination =>
|
|
29941
|
+
onMove: destination => dispatch(movePoint(i, destination))
|
|
30178
29942
|
})));
|
|
30179
29943
|
}
|
|
30180
29944
|
|
|
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
29945
|
// Get the quadratic coefficients from the 3 control points
|
|
30196
29946
|
// These equations were originally set up in 2013 and may require some
|
|
30197
29947
|
// additional comments to help clarify the quadratic formula manipulations
|
|
@@ -31335,6 +31085,203 @@ const LegacyGrid = ({
|
|
|
31335
31085
|
return null;
|
|
31336
31086
|
};
|
|
31337
31087
|
|
|
31088
|
+
function initializeGraphState(params) {
|
|
31089
|
+
var _graph$snapTo;
|
|
31090
|
+
const {
|
|
31091
|
+
graph,
|
|
31092
|
+
step,
|
|
31093
|
+
snapStep,
|
|
31094
|
+
range
|
|
31095
|
+
} = params;
|
|
31096
|
+
const shared = {
|
|
31097
|
+
hasBeenInteractedWith: false,
|
|
31098
|
+
range,
|
|
31099
|
+
snapStep
|
|
31100
|
+
};
|
|
31101
|
+
switch (graph.type) {
|
|
31102
|
+
case "segment":
|
|
31103
|
+
return _extends({}, shared, {
|
|
31104
|
+
type: "segment",
|
|
31105
|
+
coords: getSegmentCoords(graph, range, step)
|
|
31106
|
+
});
|
|
31107
|
+
case "linear":
|
|
31108
|
+
return _extends({}, shared, {
|
|
31109
|
+
type: graph.type,
|
|
31110
|
+
coords: getLineCoords(graph, range, step)
|
|
31111
|
+
});
|
|
31112
|
+
case "ray":
|
|
31113
|
+
return _extends({}, shared, {
|
|
31114
|
+
type: graph.type,
|
|
31115
|
+
coords: getLineCoords(graph, range, step)
|
|
31116
|
+
});
|
|
31117
|
+
case "linear-system":
|
|
31118
|
+
return _extends({}, shared, {
|
|
31119
|
+
type: graph.type,
|
|
31120
|
+
coords: getLinearSystemCoords(graph, range, step)
|
|
31121
|
+
});
|
|
31122
|
+
case "polygon":
|
|
31123
|
+
return _extends({}, shared, {
|
|
31124
|
+
type: "polygon",
|
|
31125
|
+
showAngles: Boolean(graph.showAngles),
|
|
31126
|
+
showSides: Boolean(graph.showSides),
|
|
31127
|
+
coords: getPolygonCoords(graph, range, step),
|
|
31128
|
+
snapTo: (_graph$snapTo = graph.snapTo) != null ? _graph$snapTo : "grid"
|
|
31129
|
+
});
|
|
31130
|
+
case "point":
|
|
31131
|
+
return _extends({}, shared, {
|
|
31132
|
+
type: graph.type,
|
|
31133
|
+
coords: getPointCoords(graph, range, step)
|
|
31134
|
+
});
|
|
31135
|
+
case "circle":
|
|
31136
|
+
return _extends({}, shared, {
|
|
31137
|
+
type: graph.type
|
|
31138
|
+
}, getCircleCoords(graph));
|
|
31139
|
+
case "quadratic":
|
|
31140
|
+
return _extends({}, shared, {
|
|
31141
|
+
type: graph.type,
|
|
31142
|
+
coords: getQuadraticCoords(graph, range, step)
|
|
31143
|
+
});
|
|
31144
|
+
case "sinusoid":
|
|
31145
|
+
return _extends({}, shared, {
|
|
31146
|
+
type: graph.type,
|
|
31147
|
+
coords: getSinusoidCoords(graph, range, step)
|
|
31148
|
+
});
|
|
31149
|
+
case "angle":
|
|
31150
|
+
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
31151
|
+
default:
|
|
31152
|
+
throw new UnreachableCaseError(graph);
|
|
31153
|
+
}
|
|
31154
|
+
}
|
|
31155
|
+
function getPointCoords(graph, range, step) {
|
|
31156
|
+
var _graph$coords;
|
|
31157
|
+
const numPoints = graph.numPoints || 1;
|
|
31158
|
+
let coords = (_graph$coords = graph.coords) == null ? void 0 : _graph$coords.slice();
|
|
31159
|
+
if (coords) {
|
|
31160
|
+
return coords;
|
|
31161
|
+
}
|
|
31162
|
+
switch (numPoints) {
|
|
31163
|
+
case 1:
|
|
31164
|
+
// Back in the day, one point's coords were in graph.coord
|
|
31165
|
+
coords = [graph.coord || [0, 0]];
|
|
31166
|
+
break;
|
|
31167
|
+
case 2:
|
|
31168
|
+
coords = [[-5, 0], [5, 0]];
|
|
31169
|
+
break;
|
|
31170
|
+
case 3:
|
|
31171
|
+
coords = [[-5, 0], [0, 0], [5, 0]];
|
|
31172
|
+
break;
|
|
31173
|
+
case 4:
|
|
31174
|
+
coords = [[-6, 0], [-2, 0], [2, 0], [6, 0]];
|
|
31175
|
+
break;
|
|
31176
|
+
case 5:
|
|
31177
|
+
coords = [[-6, 0], [-3, 0], [0, 0], [3, 0], [6, 0]];
|
|
31178
|
+
break;
|
|
31179
|
+
case 6:
|
|
31180
|
+
coords = [[-5, 0], [-3, 0], [-1, 0], [1, 0], [3, 0], [5, 0]];
|
|
31181
|
+
break;
|
|
31182
|
+
default:
|
|
31183
|
+
coords = [];
|
|
31184
|
+
break;
|
|
31185
|
+
}
|
|
31186
|
+
// Transform coords from their -10 to 10 space to 0 to 1
|
|
31187
|
+
// because of the old graph.coord, and also it's easier.
|
|
31188
|
+
const newCoords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
31189
|
+
return normalizePoints(range, step, newCoords);
|
|
31190
|
+
}
|
|
31191
|
+
function getSegmentCoords(graph, range, step) {
|
|
31192
|
+
if (graph.coords) {
|
|
31193
|
+
return graph.coords;
|
|
31194
|
+
}
|
|
31195
|
+
const ys = n => {
|
|
31196
|
+
switch (n) {
|
|
31197
|
+
case 2:
|
|
31198
|
+
return [5, -5];
|
|
31199
|
+
case 3:
|
|
31200
|
+
return [5, 0, -5];
|
|
31201
|
+
case 4:
|
|
31202
|
+
return [6, 2, -2, -6];
|
|
31203
|
+
case 5:
|
|
31204
|
+
return [6, 3, 0, -3, -6];
|
|
31205
|
+
case 6:
|
|
31206
|
+
return [5, 3, 1, -1, -3, -5];
|
|
31207
|
+
default:
|
|
31208
|
+
return [5];
|
|
31209
|
+
}
|
|
31210
|
+
};
|
|
31211
|
+
const defaultRange = [[-10, 10], [-10, 10]];
|
|
31212
|
+
return ys(graph.numSegments).map(y => {
|
|
31213
|
+
let endpoints = [[-5, y], [5, y]];
|
|
31214
|
+
endpoints = normalizeCoords(endpoints, defaultRange);
|
|
31215
|
+
endpoints = normalizePoints(range, step, endpoints);
|
|
31216
|
+
return endpoints;
|
|
31217
|
+
});
|
|
31218
|
+
}
|
|
31219
|
+
const defaultLinearCoords = [[[0.25, 0.75], [0.75, 0.75]], [[0.25, 0.25], [0.75, 0.25]]];
|
|
31220
|
+
function getLineCoords(graph, range, step) {
|
|
31221
|
+
if (graph.coords) {
|
|
31222
|
+
return graph.coords;
|
|
31223
|
+
}
|
|
31224
|
+
return normalizePoints(range, step, defaultLinearCoords[0]);
|
|
31225
|
+
}
|
|
31226
|
+
function getLinearSystemCoords(graph, range, step) {
|
|
31227
|
+
if (graph.coords) {
|
|
31228
|
+
return graph.coords;
|
|
31229
|
+
}
|
|
31230
|
+
return defaultLinearCoords.map(points => normalizePoints(range, step, points));
|
|
31231
|
+
}
|
|
31232
|
+
function getPolygonCoords(graph, range, step) {
|
|
31233
|
+
var _graph$coords2;
|
|
31234
|
+
let coords = (_graph$coords2 = graph.coords) == null ? void 0 : _graph$coords2.slice();
|
|
31235
|
+
if (coords) {
|
|
31236
|
+
return coords;
|
|
31237
|
+
}
|
|
31238
|
+
const n = graph.numSides || 3;
|
|
31239
|
+
if (n === "unlimited") {
|
|
31240
|
+
coords = [];
|
|
31241
|
+
} else {
|
|
31242
|
+
const angle = 2 * Math.PI / n;
|
|
31243
|
+
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
31244
|
+
|
|
31245
|
+
// TODO(alex): Generalize this to more than just triangles so that
|
|
31246
|
+
// all polygons have whole number side lengths if snapping to sides
|
|
31247
|
+
const radius = graph.snapTo === "sides" ? Math.sqrt(3) / 3 * 7 : 4;
|
|
31248
|
+
|
|
31249
|
+
// Generate coords of a regular polygon with n sides
|
|
31250
|
+
coords = [...Array(n).keys()].map(i => [radius * Math.cos(i * angle + offset), radius * Math.sin(i * angle + offset)]);
|
|
31251
|
+
}
|
|
31252
|
+
coords = normalizeCoords(coords, [[-10, 10], [-10, 10]]);
|
|
31253
|
+
const snapToGrid = !["angles", "sides"].includes(graph.snapTo || "");
|
|
31254
|
+
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
31255
|
+
return coords;
|
|
31256
|
+
}
|
|
31257
|
+
function getSinusoidCoords(graph, range, step) {
|
|
31258
|
+
if (graph.coords) {
|
|
31259
|
+
return [graph.coords[0], graph.coords[1]];
|
|
31260
|
+
}
|
|
31261
|
+
let coords = [[0.5, 0.5], [0.65, 0.6]];
|
|
31262
|
+
coords = normalizePoints(range, step, coords, true);
|
|
31263
|
+
return coords;
|
|
31264
|
+
}
|
|
31265
|
+
function getQuadraticCoords(graph, range, step) {
|
|
31266
|
+
if (graph.coords) {
|
|
31267
|
+
return graph.coords;
|
|
31268
|
+
}
|
|
31269
|
+
const defaultCoords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
|
|
31270
|
+
return normalizePoints(range, step, defaultCoords, true);
|
|
31271
|
+
}
|
|
31272
|
+
function getCircleCoords(graph) {
|
|
31273
|
+
if (graph.center != null && graph.radius != null) {
|
|
31274
|
+
return {
|
|
31275
|
+
center: graph.center,
|
|
31276
|
+
radiusPoint: vec.add(graph.center, [graph.radius, 0])
|
|
31277
|
+
};
|
|
31278
|
+
}
|
|
31279
|
+
return {
|
|
31280
|
+
center: [0, 0],
|
|
31281
|
+
radiusPoint: [2, 0]
|
|
31282
|
+
};
|
|
31283
|
+
}
|
|
31284
|
+
|
|
31338
31285
|
function interactiveGraphReducer(state, action) {
|
|
31339
31286
|
switch (action.type) {
|
|
31340
31287
|
case MOVE_CONTROL_POINT:
|
|
@@ -31468,11 +31415,20 @@ function doMoveAll(state, action) {
|
|
|
31468
31415
|
switch (state.type) {
|
|
31469
31416
|
case "polygon":
|
|
31470
31417
|
{
|
|
31471
|
-
|
|
31472
|
-
|
|
31473
|
-
|
|
31474
|
-
|
|
31475
|
-
|
|
31418
|
+
let newCoords;
|
|
31419
|
+
if (state.snapTo === "sides" || state.snapTo === "angles") {
|
|
31420
|
+
const change = getChange(state.coords, action.delta, {
|
|
31421
|
+
snapStep: [0, 0],
|
|
31422
|
+
range
|
|
31423
|
+
});
|
|
31424
|
+
newCoords = state.coords.map(point => vec.add(point, change));
|
|
31425
|
+
} else {
|
|
31426
|
+
const change = getChange(state.coords, action.delta, {
|
|
31427
|
+
snapStep,
|
|
31428
|
+
range
|
|
31429
|
+
});
|
|
31430
|
+
newCoords = state.coords.map(point => snap(snapStep, vec.add(point, change)));
|
|
31431
|
+
}
|
|
31476
31432
|
return _extends({}, state, {
|
|
31477
31433
|
hasBeenInteractedWith: true,
|
|
31478
31434
|
coords: newCoords
|
|
@@ -31486,10 +31442,18 @@ function doMoveAll(state, action) {
|
|
|
31486
31442
|
function doMovePoint(state, action) {
|
|
31487
31443
|
switch (state.type) {
|
|
31488
31444
|
case "polygon":
|
|
31445
|
+
let newValue;
|
|
31446
|
+
if (state.snapTo === "sides") {
|
|
31447
|
+
newValue = boundAndSnapToSides(action.destination, state, action.index);
|
|
31448
|
+
} else if (state.snapTo === "angles") {
|
|
31449
|
+
newValue = boundAndSnapToAngle(action.destination, state, action.index);
|
|
31450
|
+
} else {
|
|
31451
|
+
newValue = boundAndSnapToGrid(action.destination, state);
|
|
31452
|
+
}
|
|
31489
31453
|
const newCoords = setAtIndex({
|
|
31490
31454
|
array: state.coords,
|
|
31491
31455
|
index: action.index,
|
|
31492
|
-
newValue:
|
|
31456
|
+
newValue: newValue
|
|
31493
31457
|
});
|
|
31494
31458
|
|
|
31495
31459
|
// Reject the move if it would cause the sides of the polygon to cross
|
|
@@ -31532,6 +31496,15 @@ function doMovePoint(state, action) {
|
|
|
31532
31496
|
}
|
|
31533
31497
|
case "quadratic":
|
|
31534
31498
|
{
|
|
31499
|
+
// Set up the new coords and check if the quadratic coefficients are valid
|
|
31500
|
+
const _newCoords2 = [...state.coords];
|
|
31501
|
+
_newCoords2[action.index] = action.destination;
|
|
31502
|
+
const QuadraticCoefficients = getQuadraticCoefficients(_newCoords2);
|
|
31503
|
+
|
|
31504
|
+
// If the new destination results in an invalid quadratic equation, we don't want to move the point
|
|
31505
|
+
if (QuadraticCoefficients === undefined) {
|
|
31506
|
+
return state;
|
|
31507
|
+
}
|
|
31535
31508
|
return _extends({}, state, {
|
|
31536
31509
|
hasBeenInteractedWith: true,
|
|
31537
31510
|
coords: setAtIndex({
|
|
@@ -31624,14 +31597,8 @@ function doChangeRange(state, action) {
|
|
|
31624
31597
|
range: action.range
|
|
31625
31598
|
});
|
|
31626
31599
|
}
|
|
31627
|
-
const
|
|
31600
|
+
const getDeltaVertex = (maxMoves, minMoves, delta) => {
|
|
31628
31601
|
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
31602
|
const maxXMove = Math.min(...maxMoves.map(move => move[0]));
|
|
31636
31603
|
const maxYMove = Math.min(...maxMoves.map(move => move[1]));
|
|
31637
31604
|
const minXMove = Math.max(...minMoves.map(move => move[0]));
|
|
@@ -31640,6 +31607,22 @@ const getChange = (coords, delta, constraintOpts) => {
|
|
|
31640
31607
|
const dy = clamp(deltaY, minYMove, maxYMove);
|
|
31641
31608
|
return [dx, dy];
|
|
31642
31609
|
};
|
|
31610
|
+
const getChange = (coords, delta, constraintOpts) => {
|
|
31611
|
+
const maxMoves = coords.map(point => maxMove(_extends({}, constraintOpts, {
|
|
31612
|
+
point
|
|
31613
|
+
})));
|
|
31614
|
+
const minMoves = coords.map(point => minMove(_extends({}, constraintOpts, {
|
|
31615
|
+
point
|
|
31616
|
+
})));
|
|
31617
|
+
const [dx, dy] = getDeltaVertex(maxMoves, minMoves, delta);
|
|
31618
|
+
return [dx, dy];
|
|
31619
|
+
};
|
|
31620
|
+
const eq$1 = Util.eq;
|
|
31621
|
+
|
|
31622
|
+
// Less than or approximately equal
|
|
31623
|
+
function leq$1(a, b) {
|
|
31624
|
+
return a < b || eq$1(a, b);
|
|
31625
|
+
}
|
|
31643
31626
|
function boundAndSnapToGrid(point, {
|
|
31644
31627
|
snapStep,
|
|
31645
31628
|
range
|
|
@@ -31650,6 +31633,131 @@ function boundAndSnapToGrid(point, {
|
|
|
31650
31633
|
point
|
|
31651
31634
|
}));
|
|
31652
31635
|
}
|
|
31636
|
+
function boundAndSnapToAngle(destinationPoint, {
|
|
31637
|
+
range,
|
|
31638
|
+
coords
|
|
31639
|
+
}, index) {
|
|
31640
|
+
const startingPoint = coords[index];
|
|
31641
|
+
|
|
31642
|
+
// Needed to prevent updating the original coords before the checks for
|
|
31643
|
+
// degenerate triangles and overlapping sides
|
|
31644
|
+
const coordsCopy = [...coords];
|
|
31645
|
+
|
|
31646
|
+
// Takes the destination point and makes sure it is within the bounds of the graph
|
|
31647
|
+
// SnapStep is [0, 0] because we don't want to snap to the grid
|
|
31648
|
+
coordsCopy[index] = bound$1({
|
|
31649
|
+
snapStep: [0, 0],
|
|
31650
|
+
range,
|
|
31651
|
+
point: destinationPoint
|
|
31652
|
+
});
|
|
31653
|
+
|
|
31654
|
+
// Gets the radian angles between the coords and maps them to degrees
|
|
31655
|
+
const angles = angleMeasures(coordsCopy).map(angle => angle * 180 / Math.PI);
|
|
31656
|
+
|
|
31657
|
+
// Gets the relative index of a point
|
|
31658
|
+
const rel = j => {
|
|
31659
|
+
return (index + j + coordsCopy.length) % coordsCopy.length;
|
|
31660
|
+
};
|
|
31661
|
+
|
|
31662
|
+
// Round the angles to left and right of the current point
|
|
31663
|
+
_.each([-1, 1], function (j) {
|
|
31664
|
+
angles[rel(j)] = Math.round(angles[rel(j)]);
|
|
31665
|
+
});
|
|
31666
|
+
const getAngle = function getAngle(a, vertex, b) {
|
|
31667
|
+
const angle = GraphUtils.findAngle(coordsCopy[rel(a)], coordsCopy[rel(b)], coordsCopy[rel(vertex)]);
|
|
31668
|
+
return (angle + 360) % 360;
|
|
31669
|
+
};
|
|
31670
|
+
const innerAngles = [angles[rel(-1)] - getAngle(-2, -1, 1), angles[rel(1)] - getAngle(-1, 1, 2)];
|
|
31671
|
+
innerAngles[2] = 180 - (innerAngles[0] + innerAngles[1]);
|
|
31672
|
+
|
|
31673
|
+
// Avoid degenerate triangles
|
|
31674
|
+
if (innerAngles.some(function (angle) {
|
|
31675
|
+
return leq$1(angle, 1);
|
|
31676
|
+
})) {
|
|
31677
|
+
return startingPoint;
|
|
31678
|
+
}
|
|
31679
|
+
const knownSide = magnitude(
|
|
31680
|
+
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
31681
|
+
vector(coordsCopy[rel(-1)], coordsCopy[rel(1)]));
|
|
31682
|
+
|
|
31683
|
+
// Returns true if the points form a counter-clockwise turn;
|
|
31684
|
+
// a.k.a if the point is on the left or right of the polygon.
|
|
31685
|
+
// This is used to determine how to adjust the point: if the point is on the left,
|
|
31686
|
+
// we want to add the inner angle to the outer angle, and if it's on the right,
|
|
31687
|
+
// we want to subtract the inner angle from the outer angle. The angle solved
|
|
31688
|
+
// for is then used in the polar function to determine the new point.
|
|
31689
|
+
const onLeft = sign(ccw(coordsCopy[rel(-1)], coordsCopy[rel(1)], coordsCopy[index])) === 1;
|
|
31690
|
+
|
|
31691
|
+
// Solve for side by using the law of sines
|
|
31692
|
+
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
31693
|
+
|
|
31694
|
+
// Angle at the second vertex of the polygon
|
|
31695
|
+
const outerAngle = GraphUtils.findAngle(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
31696
|
+
|
|
31697
|
+
// Uses the length of the side of the polygon (radial coordinate)
|
|
31698
|
+
// and the angle between the first and second sides of the
|
|
31699
|
+
// polygon (angular coordinate) to determine how to adjust the point
|
|
31700
|
+
const offset = polar(side, outerAngle + (onLeft ? 1 : -1) * innerAngles[0]);
|
|
31701
|
+
return vector$1.add(coordsCopy[rel(-1)], offset);
|
|
31702
|
+
}
|
|
31703
|
+
function boundAndSnapToSides(destinationPoint, {
|
|
31704
|
+
range,
|
|
31705
|
+
coords
|
|
31706
|
+
}, index) {
|
|
31707
|
+
const startingPoint = coords[index];
|
|
31708
|
+
|
|
31709
|
+
// Needed to prevent updating the original coords before the checks for
|
|
31710
|
+
// degenerate triangles and overlapping sides
|
|
31711
|
+
const coordsCopy = [...coords];
|
|
31712
|
+
|
|
31713
|
+
// Takes the destination point and makes sure it is within the bounds of the graph
|
|
31714
|
+
// SnapStep is [0, 0] because we don't want to snap to the grid
|
|
31715
|
+
coordsCopy[index] = bound$1({
|
|
31716
|
+
snapStep: [0, 0],
|
|
31717
|
+
range,
|
|
31718
|
+
point: destinationPoint
|
|
31719
|
+
});
|
|
31720
|
+
|
|
31721
|
+
// Gets the relative index of a point
|
|
31722
|
+
const rel = j => {
|
|
31723
|
+
return (index + j + coordsCopy.length) % coordsCopy.length;
|
|
31724
|
+
};
|
|
31725
|
+
const sides = _.map([[coordsCopy[rel(-1)], coordsCopy[index]], [coordsCopy[index], coordsCopy[rel(1)]], [coordsCopy[rel(-1)], coordsCopy[rel(1)]]], function (coordsCopy) {
|
|
31726
|
+
// @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.
|
|
31727
|
+
return magnitude(vector(...coordsCopy));
|
|
31728
|
+
});
|
|
31729
|
+
|
|
31730
|
+
// Round the sides to left and right of the current point
|
|
31731
|
+
_.each([0, 1], function (j) {
|
|
31732
|
+
sides[j] = Math.round(sides[j]);
|
|
31733
|
+
});
|
|
31734
|
+
|
|
31735
|
+
// Avoid degenerate triangles
|
|
31736
|
+
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])) {
|
|
31737
|
+
return startingPoint;
|
|
31738
|
+
}
|
|
31739
|
+
|
|
31740
|
+
// Solve for angle by using the law of cosines
|
|
31741
|
+
// Angle at the first vertex of the polygon
|
|
31742
|
+
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
31743
|
+
|
|
31744
|
+
// Angle at the second vertex of the polygon
|
|
31745
|
+
const outerAngle = GraphUtils.findAngle(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
31746
|
+
|
|
31747
|
+
// Returns true if the points form a counter-clockwise turn;
|
|
31748
|
+
// a.k.a if the point is on the left or right of the polygon.
|
|
31749
|
+
// This is used to determine how to adjust the point: if the point is on the left,
|
|
31750
|
+
// we want to add the inner angle to the outer angle, and if it's on the right,
|
|
31751
|
+
// we want to subtract the inner angle from the outer angle. The angle solved
|
|
31752
|
+
// for is then used in the polar function to determine the new point.
|
|
31753
|
+
const onLeft = sign(ccw(coordsCopy[rel(-1)], coordsCopy[rel(1)], coordsCopy[index])) === 1;
|
|
31754
|
+
|
|
31755
|
+
// Uses the length of the first side of the polygon (radial coordinate)
|
|
31756
|
+
// and the angle between the first and second sides of the
|
|
31757
|
+
// polygon (angular coordinate) to determine how to adjust the point
|
|
31758
|
+
const offset = polar(sides[0], outerAngle + (onLeft ? 1 : -1) * innerAngle);
|
|
31759
|
+
return vector$1.add(coordsCopy[rel(-1)], offset);
|
|
31760
|
+
}
|
|
31653
31761
|
|
|
31654
31762
|
// Returns the closest point to the given `point` that is within the graph
|
|
31655
31763
|
// bounds given in `state`.
|
|
@@ -31664,7 +31772,7 @@ function bound$1({
|
|
|
31664
31772
|
return [clamp(requestedX, minX + snapX, maxX - snapX), clamp(requestedY, minY + snapY, maxY - snapY)];
|
|
31665
31773
|
}
|
|
31666
31774
|
|
|
31667
|
-
// Returns the vector from the given point to the top-right corner of the graph
|
|
31775
|
+
// Returns the vector from the given point to the top-right corner of the graph when snapped to the grid
|
|
31668
31776
|
function maxMove({
|
|
31669
31777
|
snapStep,
|
|
31670
31778
|
range,
|
|
@@ -31678,8 +31786,7 @@ function maxMove({
|
|
|
31678
31786
|
return vec.sub(topRight, point);
|
|
31679
31787
|
}
|
|
31680
31788
|
|
|
31681
|
-
// Returns the vector from the given point to the bottom-left corner of the
|
|
31682
|
-
// graph
|
|
31789
|
+
// Returns the vector from the given point to the bottom-left corner of the graph when snapped to the grid
|
|
31683
31790
|
function minMove({
|
|
31684
31791
|
snapStep,
|
|
31685
31792
|
range,
|
|
@@ -43994,7 +44101,7 @@ const init = function init(options) {
|
|
|
43994
44101
|
|
|
43995
44102
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
43996
44103
|
const libName = "@khanacademy/perseus";
|
|
43997
|
-
const libVersion = "23.
|
|
44104
|
+
const libVersion = "23.4.0";
|
|
43998
44105
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
43999
44106
|
|
|
44000
44107
|
const apiVersion = {
|