@khanacademy/perseus 36.1.0 → 36.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/index.js +199 -74
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +200 -73
- package/dist/index.js.map +1 -1
- package/dist/util/graphie.d.ts +1 -1
- package/dist/validation.types.d.ts +6 -4
- package/dist/widgets/cs-program/cs-program.d.ts +2 -5
- package/dist/widgets/interactive-graph.d.ts +2 -2
- package/dist/widgets/interactive-graphs/backgrounds/axis-ticks.d.ts +1 -0
- package/dist/widgets/interactive-graphs/math/angles.d.ts +3 -1
- package/dist/widgets/interactive-graphs/math/index.d.ts +1 -1
- package/package.json +9 -9
package/dist/index.d.ts
CHANGED
|
@@ -68,6 +68,7 @@ export { getWidgetTypeByWidgetId, contentHasWidgetType, getWidgetIdsFromContent,
|
|
|
68
68
|
export { convertWidgetNameToEnum } from "./util/widget-enum-utils";
|
|
69
69
|
export { addWidget, QUESTION_WIDGETS } from "./util/snowman-utils";
|
|
70
70
|
export { getCircleCoords, getLineCoords, getLinearSystemCoords, getPointCoords, getPolygonCoords, getSegmentCoords, getSinusoidCoords, getQuadraticCoords, getAngleCoords, } from "./widgets/interactive-graphs/reducer/initialize-graph-state";
|
|
71
|
+
export { getClockwiseAngle } from "./widgets/interactive-graphs/math";
|
|
71
72
|
export { makeSafeUrl } from "./widgets/phet-simulation";
|
|
72
73
|
/**
|
|
73
74
|
* Mixins
|
package/dist/index.js
CHANGED
|
@@ -3263,13 +3263,13 @@ function eq$4(x, y) {
|
|
|
3263
3263
|
* Deep approximate equality on primitives, numbers, arrays, and objects.
|
|
3264
3264
|
* Recursive.
|
|
3265
3265
|
*/
|
|
3266
|
-
function deepEq$
|
|
3266
|
+
function deepEq$6(x, y) {
|
|
3267
3267
|
if (Array.isArray(x) && Array.isArray(y)) {
|
|
3268
3268
|
if (x.length !== y.length) {
|
|
3269
3269
|
return false;
|
|
3270
3270
|
}
|
|
3271
3271
|
for (let i = 0; i < x.length; i++) {
|
|
3272
|
-
if (!deepEq$
|
|
3272
|
+
if (!deepEq$6(x[i], y[i])) {
|
|
3273
3273
|
return false;
|
|
3274
3274
|
}
|
|
3275
3275
|
}
|
|
@@ -3287,10 +3287,10 @@ function deepEq$5(x, y) {
|
|
|
3287
3287
|
if (typeof x === "object" && typeof y === "object" && !!x && !!y) {
|
|
3288
3288
|
return x === y || ___default["default"].all(x, function (v, k) {
|
|
3289
3289
|
// @ts-expect-error - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
|
|
3290
|
-
return deepEq$
|
|
3290
|
+
return deepEq$6(y[k], v);
|
|
3291
3291
|
}) && ___default["default"].all(y, function (v, k) {
|
|
3292
3292
|
// @ts-expect-error - TS2536 - Type 'CollectionKey<T>' cannot be used to index type 'T'.
|
|
3293
|
-
return deepEq$
|
|
3293
|
+
return deepEq$6(x[k], v);
|
|
3294
3294
|
});
|
|
3295
3295
|
}
|
|
3296
3296
|
if (typeof x === "object" && !!x || typeof y === "object" && !!y) {
|
|
@@ -3568,7 +3568,7 @@ const Util = {
|
|
|
3568
3568
|
tickStepFromNumTicks,
|
|
3569
3569
|
constrainedTickStepsFromTickSteps,
|
|
3570
3570
|
eq: eq$4,
|
|
3571
|
-
deepEq: deepEq$
|
|
3571
|
+
deepEq: deepEq$6,
|
|
3572
3572
|
parseQueryString,
|
|
3573
3573
|
updateQueryString: updateQueryString$2,
|
|
3574
3574
|
strongEncodeURIComponent,
|
|
@@ -10176,7 +10176,10 @@ const GraphUtils = {
|
|
|
10176
10176
|
return kmath.point.distanceToPoint(point1, point2);
|
|
10177
10177
|
},
|
|
10178
10178
|
// Find the angle in degrees between two or three points
|
|
10179
|
-
|
|
10179
|
+
// This function is deprecated as it has several issues calculating
|
|
10180
|
+
// correctly when dealing with reflex angles or the position of point2
|
|
10181
|
+
// (LEMS-2202) Remove this function while removing the Legacy Interactive Graph.
|
|
10182
|
+
findAngleDeprecated: function (point1, point2, vertex) {
|
|
10180
10183
|
if (vertex === undefined) {
|
|
10181
10184
|
const x = point1[0] - point2[0];
|
|
10182
10185
|
const y = point1[1] - point2[1];
|
|
@@ -10185,7 +10188,7 @@ const GraphUtils = {
|
|
|
10185
10188
|
}
|
|
10186
10189
|
return (180 + Math.atan2(-y, -x) * 180 / Math.PI + 360) % 360;
|
|
10187
10190
|
}
|
|
10188
|
-
return GraphUtils.
|
|
10191
|
+
return GraphUtils.findAngleDeprecated(point1, vertex) - GraphUtils.findAngleDeprecated(point2, vertex);
|
|
10189
10192
|
},
|
|
10190
10193
|
graphs: {}
|
|
10191
10194
|
};
|
|
@@ -10892,7 +10895,7 @@ class WrappedLine extends WrappedDrawing {
|
|
|
10892
10895
|
*/
|
|
10893
10896
|
const {
|
|
10894
10897
|
eq: eq$3,
|
|
10895
|
-
deepEq: deepEq$
|
|
10898
|
+
deepEq: deepEq$5
|
|
10896
10899
|
} = Util;
|
|
10897
10900
|
|
|
10898
10901
|
// This should really be a readonly tuple of [number, number]
|
|
@@ -11052,7 +11055,7 @@ function similar(coords1, coords2, tolerance) {
|
|
|
11052
11055
|
angles = rotate(angles, i);
|
|
11053
11056
|
// @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
11054
11057
|
sides = rotate(sides, i);
|
|
11055
|
-
if (deepEq$
|
|
11058
|
+
if (deepEq$5(angles1, angles)) {
|
|
11056
11059
|
const sidePairs = ___default["default"].zip(sides1, sides);
|
|
11057
11060
|
const factors = ___default["default"].map(sidePairs, function (pair) {
|
|
11058
11061
|
return pair[0] / pair[1];
|
|
@@ -11202,8 +11205,8 @@ ___default["default"].extend(GraphUtils.Graphie.prototype, {
|
|
|
11202
11205
|
p1 = options.point3;
|
|
11203
11206
|
p3 = options.point1;
|
|
11204
11207
|
}
|
|
11205
|
-
const startAngle = GraphUtils.
|
|
11206
|
-
const endAngle = GraphUtils.
|
|
11208
|
+
const startAngle = GraphUtils.findAngleDeprecated(p1, vertex);
|
|
11209
|
+
const endAngle = GraphUtils.findAngleDeprecated(p3, vertex);
|
|
11207
11210
|
const angle = (endAngle + 360 - startAngle) % 360;
|
|
11208
11211
|
const halfAngle = (startAngle + angle / 2) % 360;
|
|
11209
11212
|
const sPadding = 5 * options.pushOut;
|
|
@@ -11370,15 +11373,15 @@ ___default["default"].extend(GraphUtils.Graphie.prototype, {
|
|
|
11370
11373
|
let angle = 135;
|
|
11371
11374
|
let halfAngle;
|
|
11372
11375
|
if (p1 && p3) {
|
|
11373
|
-
const startAngle = GraphUtils.
|
|
11374
|
-
const endAngle = GraphUtils.
|
|
11376
|
+
const startAngle = GraphUtils.findAngleDeprecated(p1, vertex);
|
|
11377
|
+
const endAngle = GraphUtils.findAngleDeprecated(p3, vertex);
|
|
11375
11378
|
angle = (endAngle + 360 - startAngle) % 360;
|
|
11376
11379
|
halfAngle = (startAngle + angle / 2 + 180) % 360;
|
|
11377
11380
|
} else if (p1) {
|
|
11378
|
-
const parallelAngle = GraphUtils.
|
|
11381
|
+
const parallelAngle = GraphUtils.findAngleDeprecated(vertex, p1);
|
|
11379
11382
|
halfAngle = parallelAngle + 90;
|
|
11380
11383
|
} else if (p3) {
|
|
11381
|
-
const parallelAngle = GraphUtils.
|
|
11384
|
+
const parallelAngle = GraphUtils.findAngleDeprecated(p3, vertex);
|
|
11382
11385
|
halfAngle = parallelAngle + 90;
|
|
11383
11386
|
} else {
|
|
11384
11387
|
// Standalone point
|
|
@@ -11584,7 +11587,7 @@ ___default["default"].extend(GraphUtils.Graphie.prototype, {
|
|
|
11584
11587
|
const vertex = constraints.fixedAngle.vertex.coord || constraints.fixedAngle.vertex;
|
|
11585
11588
|
const ref = constraints.fixedAngle.ref.coord || constraints.fixedAngle.ref;
|
|
11586
11589
|
const distPoint = constraints.fixedDistance.point.coord || constraints.fixedDistance.point;
|
|
11587
|
-
const constrainedAngle = (constraints.fixedAngle.angle + GraphUtils.
|
|
11590
|
+
const constrainedAngle = (constraints.fixedAngle.angle + GraphUtils.findAngleDeprecated(ref, vertex)) * Math.PI / 180;
|
|
11588
11591
|
const length = constraints.fixedDistance.dist;
|
|
11589
11592
|
newCoord[0] = length * Math.cos(constrainedAngle) + distPoint[0];
|
|
11590
11593
|
newCoord[1] = length * Math.sin(constrainedAngle) + distPoint[1];
|
|
@@ -11593,8 +11596,8 @@ ___default["default"].extend(GraphUtils.Graphie.prototype, {
|
|
|
11593
11596
|
} else if (typeof constraints.fixedAngle.angle === "number") {
|
|
11594
11597
|
const vertex = constraints.fixedAngle.vertex.coord || constraints.fixedAngle.vertex;
|
|
11595
11598
|
const ref = constraints.fixedAngle.ref.coord || constraints.fixedAngle.ref;
|
|
11596
|
-
const constrainedAngle = (constraints.fixedAngle.angle + GraphUtils.
|
|
11597
|
-
const angle = GraphUtils.
|
|
11599
|
+
const constrainedAngle = (constraints.fixedAngle.angle + GraphUtils.findAngleDeprecated(ref, vertex)) * Math.PI / 180;
|
|
11600
|
+
const angle = GraphUtils.findAngleDeprecated(coord, vertex) * Math.PI / 180;
|
|
11598
11601
|
const distance = GraphUtils.getDistance(coord, vertex);
|
|
11599
11602
|
let length = distance * Math.cos(constrainedAngle - angle);
|
|
11600
11603
|
length = length < 1.0 ? 1.0 : length;
|
|
@@ -11604,7 +11607,7 @@ ___default["default"].extend(GraphUtils.Graphie.prototype, {
|
|
|
11604
11607
|
// distance is constrained
|
|
11605
11608
|
} else if (typeof constraints.fixedDistance.dist === "number") {
|
|
11606
11609
|
const distPoint = constraints.fixedDistance.point.coord || constraints.fixedDistance.point;
|
|
11607
|
-
let angle = GraphUtils.
|
|
11610
|
+
let angle = GraphUtils.findAngleDeprecated(coord, distPoint);
|
|
11608
11611
|
const length = constraints.fixedDistance.dist;
|
|
11609
11612
|
angle = angle * Math.PI / 180;
|
|
11610
11613
|
newCoord[0] = length * Math.cos(angle) + distPoint[0];
|
|
@@ -13804,7 +13807,7 @@ ___default["default"].extend(MovableAngle.prototype, {
|
|
|
13804
13807
|
___default["default"].each([0, 2], function (i) {
|
|
13805
13808
|
const oldPoint = points[i].coord;
|
|
13806
13809
|
let newPoint = kmath.vector.add(oldPoint, delta);
|
|
13807
|
-
let angle = GraphUtils.
|
|
13810
|
+
let angle = GraphUtils.findAngleDeprecated(newVertex, newPoint);
|
|
13808
13811
|
angle *= Math.PI / 180;
|
|
13809
13812
|
newPoint = graphie.constrainToBoundsOnAngle(newPoint, 10, angle);
|
|
13810
13813
|
newPoints[i] = newPoint;
|
|
@@ -13837,7 +13840,7 @@ ___default["default"].extend(MovableAngle.prototype, {
|
|
|
13837
13840
|
return false;
|
|
13838
13841
|
}
|
|
13839
13842
|
if (snap) {
|
|
13840
|
-
let angle = GraphUtils.
|
|
13843
|
+
let angle = GraphUtils.findAngleDeprecated(newPoint, vertex);
|
|
13841
13844
|
angle = Math.round((angle - snapOffset) / snap) * snap + snapOffset;
|
|
13842
13845
|
const distance = GraphUtils.getDistance(newPoint, vertex);
|
|
13843
13846
|
return kmath.vector.add(vertex, polar$1(distance, angle));
|
|
@@ -13925,7 +13928,7 @@ ___default["default"].extend(MovableAngle.prototype, {
|
|
|
13925
13928
|
* coords when interpreted in a clockwise direction.
|
|
13926
13929
|
*/
|
|
13927
13930
|
_getClockwiseAngle: function (coords) {
|
|
13928
|
-
const rawAngle = GraphUtils.
|
|
13931
|
+
const rawAngle = GraphUtils.findAngleDeprecated(
|
|
13929
13932
|
// The order of these is "weird" to match what a clockwise
|
|
13930
13933
|
// order is in graphie.labelAngle
|
|
13931
13934
|
coords[2],
|
|
@@ -13991,7 +13994,7 @@ ___default["default"].extend(MovableAngle.prototype, {
|
|
|
13991
13994
|
|
|
13992
13995
|
/* eslint-disable @babel/no-invalid-this */
|
|
13993
13996
|
const nestedMap$1 = Util.nestedMap;
|
|
13994
|
-
const deepEq$
|
|
13997
|
+
const deepEq$4 = Util.deepEq;
|
|
13995
13998
|
|
|
13996
13999
|
/**
|
|
13997
14000
|
* A base class for all Graphie Movables
|
|
@@ -14088,7 +14091,7 @@ const createSimpleClass = function (addFunction) {
|
|
|
14088
14091
|
this._prevProps = this.props;
|
|
14089
14092
|
},
|
|
14090
14093
|
modify: function (graphie) {
|
|
14091
|
-
if (!deepEq$
|
|
14094
|
+
if (!deepEq$4(this.props, this._prevProps)) {
|
|
14092
14095
|
this.remove();
|
|
14093
14096
|
this.add(graphie);
|
|
14094
14097
|
this._prevProps = this.props;
|
|
@@ -16835,7 +16838,7 @@ var Movables = {
|
|
|
16835
16838
|
const GraphieMovable = GraphieClasses.GraphieMovable;
|
|
16836
16839
|
const createGraphie = GraphUtils.createGraphie;
|
|
16837
16840
|
const {
|
|
16838
|
-
deepEq: deepEq$
|
|
16841
|
+
deepEq: deepEq$3,
|
|
16839
16842
|
nestedMap
|
|
16840
16843
|
} = Util;
|
|
16841
16844
|
const {
|
|
@@ -16873,7 +16876,7 @@ class Graphie extends React__namespace.Component {
|
|
|
16873
16876
|
if (this.props.setup !== prevProps.setup) {
|
|
16874
16877
|
Log.error("<Graphie> was given a new setup function. " + "This is a bad idea; please refactor your code to give " + "the same setup function reference to <Graphie> on " + "every render.", perseusCore.Errors.Internal);
|
|
16875
16878
|
}
|
|
16876
|
-
if (!deepEq$
|
|
16879
|
+
if (!deepEq$3(this.props.options, prevProps.options) || !deepEq$3(this.props.box, prevProps.box) || !deepEq$3(this.props.range, prevProps.range)) {
|
|
16877
16880
|
this._setupGraphie();
|
|
16878
16881
|
}
|
|
16879
16882
|
this._updateMovables();
|
|
@@ -30975,16 +30978,40 @@ function polar(r, th) {
|
|
|
30975
30978
|
th = th * Math.PI / 180;
|
|
30976
30979
|
return [r[0] * Math.cos(th), r[1] * Math.sin(th)];
|
|
30977
30980
|
}
|
|
30978
|
-
|
|
30979
|
-
|
|
30980
|
-
|
|
30981
|
-
|
|
30982
|
-
|
|
30983
|
-
|
|
30984
|
-
|
|
30985
|
-
return
|
|
30981
|
+
// This function calculates the angle between two points and an optional vertex.
|
|
30982
|
+
// If the vertex is not provided, the angle is measured between the two points.
|
|
30983
|
+
// This does not account for reflex angles or clockwise position.
|
|
30984
|
+
const getAngleFromVertex = (point, vertex) => {
|
|
30985
|
+
const x = point[0] - vertex[0];
|
|
30986
|
+
const y = point[1] - vertex[1];
|
|
30987
|
+
if (!x && !y) {
|
|
30988
|
+
return 0;
|
|
30986
30989
|
}
|
|
30987
|
-
return
|
|
30990
|
+
return (180 + Math.atan2(-y, -x) * 180 / Math.PI + 360) % 360;
|
|
30991
|
+
};
|
|
30992
|
+
|
|
30993
|
+
// This function calculates the clockwise angle between three points,
|
|
30994
|
+
// and is used to generate the labels and equation strings of the
|
|
30995
|
+
// current angle for the interactive graph.
|
|
30996
|
+
const getClockwiseAngle = function (coords) {
|
|
30997
|
+
let allowReflexAngles = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
30998
|
+
const coordsCopy = [...coords];
|
|
30999
|
+
// The coords are saved as [point1, vertex, point2] in the interactive graph
|
|
31000
|
+
const areClockwise = clockwise$1([coordsCopy[0], coordsCopy[2], coordsCopy[1]]);
|
|
31001
|
+
|
|
31002
|
+
// We may need to reverse the coordinates if we allow
|
|
31003
|
+
// reflex angles and the points are not in clockwise order.
|
|
31004
|
+
const shouldReverseCoords = !areClockwise || allowReflexAngles;
|
|
31005
|
+
|
|
31006
|
+
// Reverse the coordinates accordingly to ensure the angle is calculated correctly
|
|
31007
|
+
const clockwiseCoords = shouldReverseCoords ? coordsCopy.reverse() : coordsCopy;
|
|
31008
|
+
|
|
31009
|
+
// Calculate the angles between the two points and get the difference
|
|
31010
|
+
// between the two angles to get the clockwise angle.
|
|
31011
|
+
const startAngle = getAngleFromVertex(clockwiseCoords[0], clockwiseCoords[1]);
|
|
31012
|
+
const endAngle = getAngleFromVertex(clockwiseCoords[2], clockwiseCoords[1]);
|
|
31013
|
+
const angle = (startAngle + 360 - endAngle) % 360;
|
|
31014
|
+
return angle;
|
|
30988
31015
|
};
|
|
30989
31016
|
|
|
30990
31017
|
const defaultGraphConfig = {
|
|
@@ -31299,10 +31326,16 @@ const shouldShowLabel = (currentTick, range, tickStep) => {
|
|
|
31299
31326
|
function generateTickLocations(tickStep, min, max) {
|
|
31300
31327
|
const ticks = [];
|
|
31301
31328
|
|
|
31329
|
+
// Calculate the number of significant decimals in the tick step so
|
|
31330
|
+
// that we can match the desired precision when generating ticks.
|
|
31331
|
+
const decimalSigFigs = countSignificantDecimals(tickStep);
|
|
31332
|
+
|
|
31302
31333
|
// Add ticks in the positive direction
|
|
31303
31334
|
const start = Math.max(min, 0);
|
|
31304
31335
|
for (let i = start + tickStep; i < max; i += tickStep) {
|
|
31305
|
-
|
|
31336
|
+
// Match to the same number of decimal places as the tick step
|
|
31337
|
+
// to avoid floating point errors when working with small numbers
|
|
31338
|
+
ticks.push(parseFloat(i.toFixed(decimalSigFigs)));
|
|
31306
31339
|
}
|
|
31307
31340
|
|
|
31308
31341
|
// Add ticks in the negative direction
|
|
@@ -31313,6 +31346,15 @@ function generateTickLocations(tickStep, min, max) {
|
|
|
31313
31346
|
}
|
|
31314
31347
|
return ticks;
|
|
31315
31348
|
}
|
|
31349
|
+
|
|
31350
|
+
// Count the number of significant digits after the decimal point
|
|
31351
|
+
const countSignificantDecimals = number => {
|
|
31352
|
+
const numStr = number.toString();
|
|
31353
|
+
if (!numStr.includes(".")) {
|
|
31354
|
+
return 0;
|
|
31355
|
+
}
|
|
31356
|
+
return numStr.split(".")[1].length;
|
|
31357
|
+
};
|
|
31316
31358
|
const AxisTicks = () => {
|
|
31317
31359
|
const {
|
|
31318
31360
|
tickStep,
|
|
@@ -33401,8 +33443,8 @@ const Angle = _ref2 => {
|
|
|
33401
33443
|
const clockwiseCoords = shouldReverseCoords ? coords : coords.reverse();
|
|
33402
33444
|
|
|
33403
33445
|
// Calculate the angles between the two points
|
|
33404
|
-
const startAngle =
|
|
33405
|
-
const endAngle =
|
|
33446
|
+
const startAngle = getAngleFromVertex(clockwiseCoords[0], vertex);
|
|
33447
|
+
const endAngle = getAngleFromVertex(clockwiseCoords[1], vertex);
|
|
33406
33448
|
const angle = (startAngle + 360 - endAngle) % 360;
|
|
33407
33449
|
|
|
33408
33450
|
// Check if the angle is reflexive
|
|
@@ -34491,7 +34533,9 @@ const MafsGraph = props => {
|
|
|
34491
34533
|
analytics.onAnalyticsEvent({
|
|
34492
34534
|
type: "perseus:interactive-graph-widget:rendered",
|
|
34493
34535
|
payload: {
|
|
34494
|
-
type
|
|
34536
|
+
type,
|
|
34537
|
+
widgetType: "INTERACTIVE_GRAPH",
|
|
34538
|
+
widgetId: "interactive-graph"
|
|
34495
34539
|
}
|
|
34496
34540
|
});
|
|
34497
34541
|
});
|
|
@@ -35778,7 +35822,7 @@ function boundAndSnapAngleVertex(_ref2, _ref3) {
|
|
|
35778
35822
|
for (const i of [0, 2]) {
|
|
35779
35823
|
const oldPoint = coordsCopy[i];
|
|
35780
35824
|
let newPoint = mafs.vec.add(oldPoint, delta);
|
|
35781
|
-
let angle =
|
|
35825
|
+
let angle = getAngleFromVertex(newVertex, newPoint);
|
|
35782
35826
|
angle *= Math.PI / 180;
|
|
35783
35827
|
newPoint = constrainToBoundsOnAngle(newPoint, angle, range, snapStep);
|
|
35784
35828
|
newPoints[i] = newPoint;
|
|
@@ -35851,7 +35895,7 @@ function boundAndSnapAngleEndPoints(destinationPoint, _ref5, index) {
|
|
|
35851
35895
|
const vertex = coords[1];
|
|
35852
35896
|
|
|
35853
35897
|
// Gets the angle between the coords and the vertex
|
|
35854
|
-
let angle =
|
|
35898
|
+
let angle = getAngleFromVertex(coordsCopy[index], vertex);
|
|
35855
35899
|
|
|
35856
35900
|
// Snap the angle to the nearest multiple of snapDegrees (if provided)
|
|
35857
35901
|
angle = Math.round((angle - offsetDegrees) / snap) * snap + offsetDegrees;
|
|
@@ -35898,8 +35942,8 @@ function boundAndSnapToPolygonAngle(destinationPoint, _ref6, index) {
|
|
|
35898
35942
|
angles[rel(j)] = Math.round(angles[rel(j)]);
|
|
35899
35943
|
});
|
|
35900
35944
|
const getAngle = function (a, vertex, b) {
|
|
35901
|
-
const angle =
|
|
35902
|
-
return
|
|
35945
|
+
const angle = getClockwiseAngle([coordsCopy[rel(a)], coordsCopy[rel(vertex)], coordsCopy[rel(b)]]);
|
|
35946
|
+
return angle;
|
|
35903
35947
|
};
|
|
35904
35948
|
const innerAngles = [angles[rel(-1)] - getAngle(-2, -1, 1), angles[rel(1)] - getAngle(-1, 1, 2)];
|
|
35905
35949
|
innerAngles[2] = 180 - (innerAngles[0] + innerAngles[1]);
|
|
@@ -35926,7 +35970,7 @@ function boundAndSnapToPolygonAngle(destinationPoint, _ref6, index) {
|
|
|
35926
35970
|
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
35927
35971
|
|
|
35928
35972
|
// Angle at the second vertex of the polygon
|
|
35929
|
-
const outerAngle =
|
|
35973
|
+
const outerAngle = getAngleFromVertex(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
35930
35974
|
|
|
35931
35975
|
// Uses the length of the side of the polygon (radial coordinate)
|
|
35932
35976
|
// and the angle between the first and second sides of the
|
|
@@ -35977,7 +36021,7 @@ function boundAndSnapToSides(destinationPoint, _ref7, index) {
|
|
|
35977
36021
|
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
35978
36022
|
|
|
35979
36023
|
// Angle at the second vertex of the polygon
|
|
35980
|
-
const outerAngle =
|
|
36024
|
+
const outerAngle = getAngleFromVertex(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
35981
36025
|
|
|
35982
36026
|
// Returns true if the points form a counter-clockwise turn;
|
|
35983
36027
|
// a.k.a if the point is on the left or right of the polygon.
|
|
@@ -36086,7 +36130,8 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
|
|
|
36086
36130
|
const numPoints = graph.type === "point" ? graph.numPoints : null;
|
|
36087
36131
|
const numSides = graph.type === "polygon" ? graph.numSides : null;
|
|
36088
36132
|
const snapTo = graph.type === "polygon" ? graph.snapTo : null;
|
|
36089
|
-
const showAngles = graph.type === "polygon" ? graph.showAngles : null;
|
|
36133
|
+
const showAngles = graph.type === "polygon" || graph.type === "angle" ? graph.showAngles : null;
|
|
36134
|
+
const allowReflexAngles = graph.type === "angle" ? graph.allowReflexAngles : null;
|
|
36090
36135
|
const showSides = graph.type === "polygon" ? graph.showSides : null;
|
|
36091
36136
|
const startCoords = "startCoords" in graph ? graph.startCoords : undefined;
|
|
36092
36137
|
const originalPropsRef = React.useRef(props);
|
|
@@ -36099,7 +36144,7 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
|
|
|
36099
36144
|
if (latestPropsRef.current !== originalPropsRef.current) {
|
|
36100
36145
|
dispatch(reinitialize(latestPropsRef.current));
|
|
36101
36146
|
}
|
|
36102
|
-
}, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, startCoords]);
|
|
36147
|
+
}, [graph.type, numPoints, numSegments, numSides, snapTo, showAngles, showSides, latestPropsRef, startCoords, allowReflexAngles]);
|
|
36103
36148
|
|
|
36104
36149
|
// If the graph is static, it always displays the correct answer. This is
|
|
36105
36150
|
// standard behavior for Perseus widgets (e.g. compare the Radio widget).
|
|
@@ -36119,7 +36164,7 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
|
|
|
36119
36164
|
});
|
|
36120
36165
|
|
|
36121
36166
|
const eq$1 = Util.eq;
|
|
36122
|
-
const deepEq$
|
|
36167
|
+
const deepEq$2 = Util.deepEq;
|
|
36123
36168
|
function interactiveGraphValidator(userInput, rubric) {
|
|
36124
36169
|
// None-type graphs are not graded
|
|
36125
36170
|
if (userInput.type === "none" && rubric.correct.type === "none") {
|
|
@@ -36175,7 +36220,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36175
36220
|
const correctCoeffs = getQuadraticCoefficients(
|
|
36176
36221
|
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
36177
36222
|
rubric.correct.coords);
|
|
36178
|
-
if (deepEq$
|
|
36223
|
+
if (deepEq$2(guessCoeffs, correctCoeffs)) {
|
|
36179
36224
|
return {
|
|
36180
36225
|
type: "points",
|
|
36181
36226
|
earned: 1,
|
|
@@ -36191,7 +36236,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36191
36236
|
const canonicalGuessCoeffs = canonicalSineCoefficients$1(guessCoeffs);
|
|
36192
36237
|
const canonicalCorrectCoeffs = canonicalSineCoefficients$1(correctCoeffs);
|
|
36193
36238
|
// If the canonical coefficients match, it's correct.
|
|
36194
|
-
if (deepEq$
|
|
36239
|
+
if (deepEq$2(canonicalGuessCoeffs, canonicalCorrectCoeffs)) {
|
|
36195
36240
|
return {
|
|
36196
36241
|
type: "points",
|
|
36197
36242
|
earned: 1,
|
|
@@ -36200,7 +36245,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36200
36245
|
};
|
|
36201
36246
|
}
|
|
36202
36247
|
} else if (userInput.type === "circle" && rubric.correct.type === "circle") {
|
|
36203
|
-
if (deepEq$
|
|
36248
|
+
if (deepEq$2(userInput.center, rubric.correct.center) && eq$1(userInput.radius, rubric.correct.radius)) {
|
|
36204
36249
|
return {
|
|
36205
36250
|
type: "points",
|
|
36206
36251
|
earned: 1,
|
|
@@ -36222,7 +36267,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36222
36267
|
guess?.sort();
|
|
36223
36268
|
// @ts-expect-error - TS2339 - Property 'sort' does not exist on type 'readonly Coord[]'.
|
|
36224
36269
|
correct.sort();
|
|
36225
|
-
if (deepEq$
|
|
36270
|
+
if (deepEq$2(guess, correct)) {
|
|
36226
36271
|
return {
|
|
36227
36272
|
type: "points",
|
|
36228
36273
|
earned: 1,
|
|
@@ -36245,7 +36290,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36245
36290
|
/* exact */
|
|
36246
36291
|
guess.sort();
|
|
36247
36292
|
correct.sort();
|
|
36248
|
-
match = deepEq$
|
|
36293
|
+
match = deepEq$2(guess, correct);
|
|
36249
36294
|
}
|
|
36250
36295
|
if (match) {
|
|
36251
36296
|
return {
|
|
@@ -36261,7 +36306,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36261
36306
|
guess = ___default["default"].invoke(guess, "sort").sort();
|
|
36262
36307
|
// @ts-expect-error - TS2345 - Argument of type '(readonly Coord[])[] | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
36263
36308
|
correct = ___default["default"].invoke(correct, "sort").sort();
|
|
36264
|
-
if (deepEq$
|
|
36309
|
+
if (deepEq$2(guess, correct)) {
|
|
36265
36310
|
return {
|
|
36266
36311
|
type: "points",
|
|
36267
36312
|
earned: 1,
|
|
@@ -36274,7 +36319,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36274
36319
|
const correct = rubric.correct.coords;
|
|
36275
36320
|
if (
|
|
36276
36321
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
36277
|
-
deepEq$
|
|
36322
|
+
deepEq$2(guess[0], correct[0]) &&
|
|
36278
36323
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
36279
36324
|
collinear(correct[0], correct[1], guess[1])) {
|
|
36280
36325
|
return {
|
|
@@ -36287,17 +36332,15 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36287
36332
|
} else if (userInput.type === "angle" && rubric.correct.type === "angle") {
|
|
36288
36333
|
const guess = userInput.coords;
|
|
36289
36334
|
const correct = rubric.correct.coords;
|
|
36335
|
+
const allowReflexAngles = rubric.correct.allowReflexAngles;
|
|
36290
36336
|
let match;
|
|
36291
36337
|
if (rubric.correct.match === "congruent") {
|
|
36292
36338
|
const angles = ___default["default"].map([guess, correct], function (coords) {
|
|
36293
|
-
|
|
36294
|
-
|
|
36295
|
-
|
|
36296
|
-
|
|
36297
|
-
|
|
36298
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
36299
|
-
coords[1]);
|
|
36300
|
-
return (angle + 360) % 360;
|
|
36339
|
+
if (!coords) {
|
|
36340
|
+
return false;
|
|
36341
|
+
}
|
|
36342
|
+
const angle = getClockwiseAngle(coords, allowReflexAngles);
|
|
36343
|
+
return angle;
|
|
36301
36344
|
});
|
|
36302
36345
|
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
36303
36346
|
match = eq$1(...angles);
|
|
@@ -36305,7 +36348,7 @@ function interactiveGraphValidator(userInput, rubric) {
|
|
|
36305
36348
|
/* exact */
|
|
36306
36349
|
match =
|
|
36307
36350
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
36308
|
-
deepEq$
|
|
36351
|
+
deepEq$2(guess[1], correct[1]) &&
|
|
36309
36352
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
36310
36353
|
collinear(correct[1], correct[0], guess[0]) &&
|
|
36311
36354
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
@@ -37200,7 +37243,7 @@ class LegacyInteractiveGraph extends React__namespace.Component {
|
|
|
37200
37243
|
angles[rel(j)] = Math.round(angles[rel(j)]);
|
|
37201
37244
|
});
|
|
37202
37245
|
const getAngle = function (a, vertex, b) {
|
|
37203
|
-
const angle = GraphUtils.
|
|
37246
|
+
const angle = GraphUtils.findAngleDeprecated(coords[rel(a)], coords[rel(b)], coords[rel(vertex)]);
|
|
37204
37247
|
return (angle + 360) % 360;
|
|
37205
37248
|
};
|
|
37206
37249
|
const innerAngles = [angles[rel(-1)] - getAngle(-2, -1, 1), angles[rel(1)] - getAngle(-1, 1, 2)];
|
|
@@ -37219,7 +37262,7 @@ class LegacyInteractiveGraph extends React__namespace.Component {
|
|
|
37219
37262
|
|
|
37220
37263
|
// Solve for side by using the law of sines
|
|
37221
37264
|
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
37222
|
-
const outerAngle = GraphUtils.
|
|
37265
|
+
const outerAngle = GraphUtils.findAngleDeprecated(coords[rel(1)], coords[rel(-1)]);
|
|
37223
37266
|
const offset = polar$1(side, outerAngle + (onLeft ? 1 : -1) * innerAngles[0]);
|
|
37224
37267
|
return this.graphie?.addPoints(coords[rel(-1)], offset);
|
|
37225
37268
|
}
|
|
@@ -37242,7 +37285,7 @@ class LegacyInteractiveGraph extends React__namespace.Component {
|
|
|
37242
37285
|
|
|
37243
37286
|
// Solve for angle by using the law of cosines
|
|
37244
37287
|
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
37245
|
-
const outerAngle = GraphUtils.
|
|
37288
|
+
const outerAngle = GraphUtils.findAngleDeprecated(coords[rel(1)], coords[rel(-1)]);
|
|
37246
37289
|
const onLeft = sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
|
|
37247
37290
|
const offset = polar$1(sides[0], outerAngle + (onLeft ? 1 : -1) * innerAngle);
|
|
37248
37291
|
return this.graphie?.addPoints(coords[rel(-1)], offset);
|
|
@@ -37966,7 +38009,8 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
37966
38009
|
throw makeInvalidTypeError("getAngleEquationString", "angle");
|
|
37967
38010
|
}
|
|
37968
38011
|
const coords = InteractiveGraph.getAngleCoords(props.graph, props);
|
|
37969
|
-
const
|
|
38012
|
+
const allowReflexAngles = props.graph.allowReflexAngles;
|
|
38013
|
+
const angle = getClockwiseAngle(coords, allowReflexAngles);
|
|
37970
38014
|
return angle.toFixed(0) + "\u00B0 angle" + " at (" + coords[1].join(", ") + ")";
|
|
37971
38015
|
}
|
|
37972
38016
|
static validate(userInput, rubric) {
|
|
@@ -43806,10 +43850,10 @@ var PhetSimulation$1 = {
|
|
|
43806
43850
|
};
|
|
43807
43851
|
|
|
43808
43852
|
const {
|
|
43809
|
-
deepEq
|
|
43853
|
+
deepEq: deepEq$1
|
|
43810
43854
|
} = Util;
|
|
43811
43855
|
function plotterValidator(userInput, rubric) {
|
|
43812
|
-
if (deepEq(userInput, rubric.starting)) {
|
|
43856
|
+
if (deepEq$1(userInput, rubric.starting)) {
|
|
43813
43857
|
return {
|
|
43814
43858
|
type: "invalid",
|
|
43815
43859
|
message: null
|
|
@@ -43817,7 +43861,7 @@ function plotterValidator(userInput, rubric) {
|
|
|
43817
43861
|
}
|
|
43818
43862
|
return {
|
|
43819
43863
|
type: "points",
|
|
43820
|
-
earned: deepEq(userInput, rubric.correct) ? 1 : 0,
|
|
43864
|
+
earned: deepEq$1(userInput, rubric.correct) ? 1 : 0,
|
|
43821
43865
|
total: 1,
|
|
43822
43866
|
message: null
|
|
43823
43867
|
};
|
|
@@ -45294,7 +45338,7 @@ const init = function (options) {
|
|
|
45294
45338
|
|
|
45295
45339
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45296
45340
|
const libName = "@khanacademy/perseus";
|
|
45297
|
-
const libVersion = "36.1.
|
|
45341
|
+
const libVersion = "36.1.2";
|
|
45298
45342
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45299
45343
|
|
|
45300
45344
|
const apiVersion = {
|
|
@@ -48572,6 +48616,8 @@ const getValidWidgetIds = perseusItem => {
|
|
|
48572
48616
|
return wonderStuffCore.keys(widgets).filter(id => isWidgetIdInContent(perseusItem, id));
|
|
48573
48617
|
};
|
|
48574
48618
|
|
|
48619
|
+
const deepEq = Util.deepEq;
|
|
48620
|
+
|
|
48575
48621
|
/**
|
|
48576
48622
|
* Helper to parse PerseusItem JSON
|
|
48577
48623
|
* Why not just use JSON.parse? We want:
|
|
@@ -48581,7 +48627,87 @@ const getValidWidgetIds = perseusItem => {
|
|
|
48581
48627
|
* @returns {PerseusItem} the parsed PerseusItem object
|
|
48582
48628
|
*/
|
|
48583
48629
|
function parsePerseusItem(json) {
|
|
48584
|
-
|
|
48630
|
+
const randomPhrase = buildRandomPhrase();
|
|
48631
|
+
const randomHintPhrase = buildRandomPhrase();
|
|
48632
|
+
const randomString = buildRandomString();
|
|
48633
|
+
const testingObject = JSON.stringify({
|
|
48634
|
+
answerArea: {
|
|
48635
|
+
calculator: false,
|
|
48636
|
+
chi2Table: false,
|
|
48637
|
+
financialCalculatorMonthlyPayment: false,
|
|
48638
|
+
financialCalculatorTimeToPayOff: false,
|
|
48639
|
+
financialCalculatorTotalAmount: false,
|
|
48640
|
+
periodicTable: false,
|
|
48641
|
+
periodicTableWithKey: false,
|
|
48642
|
+
tTable: false,
|
|
48643
|
+
zTable: false
|
|
48644
|
+
},
|
|
48645
|
+
hints: [randomHintPhrase, `=${Math.floor(Math.random() * 50) + 1}`],
|
|
48646
|
+
itemDataVersion: {
|
|
48647
|
+
major: 0,
|
|
48648
|
+
minor: 1
|
|
48649
|
+
},
|
|
48650
|
+
question: {
|
|
48651
|
+
content: `${randomPhrase}`,
|
|
48652
|
+
images: {},
|
|
48653
|
+
widgets: {
|
|
48654
|
+
expression1: {
|
|
48655
|
+
alignment: "default",
|
|
48656
|
+
graded: false,
|
|
48657
|
+
options: {
|
|
48658
|
+
answerForms: [{
|
|
48659
|
+
considered: "wrong",
|
|
48660
|
+
form: false,
|
|
48661
|
+
key: 0,
|
|
48662
|
+
simplify: false,
|
|
48663
|
+
value: `${randomString}`
|
|
48664
|
+
}],
|
|
48665
|
+
ariaLabel: "Answer",
|
|
48666
|
+
buttonSets: ["basic"],
|
|
48667
|
+
functions: ["f", "g", "h"],
|
|
48668
|
+
static: true,
|
|
48669
|
+
times: false,
|
|
48670
|
+
visibleLabel: "Answer"
|
|
48671
|
+
},
|
|
48672
|
+
static: true,
|
|
48673
|
+
type: "expression",
|
|
48674
|
+
version: {
|
|
48675
|
+
major: 1,
|
|
48676
|
+
minor: 0
|
|
48677
|
+
}
|
|
48678
|
+
}
|
|
48679
|
+
}
|
|
48680
|
+
}
|
|
48681
|
+
});
|
|
48682
|
+
// @ts-expect-error TS2550: Property 'replaceAll' does not exist on type 'string'.
|
|
48683
|
+
const testJSON = buildTestData(testingObject.replaceAll('"', '\\"'));
|
|
48684
|
+
const parsedJSON = JSON.parse(testJSON);
|
|
48685
|
+
const parsedItemData = parsedJSON.data.assessmentItem.item.itemData;
|
|
48686
|
+
const isNotCheating = deepEq(parsedItemData, testingObject);
|
|
48687
|
+
if (isNotCheating) {
|
|
48688
|
+
return JSON.parse(json);
|
|
48689
|
+
}
|
|
48690
|
+
throw new Error("Something went wrong.");
|
|
48691
|
+
}
|
|
48692
|
+
function buildRandomString() {
|
|
48693
|
+
let randomString = "";
|
|
48694
|
+
const randomLength = Math.floor(Math.random() * 8) + 3;
|
|
48695
|
+
for (let i = 0; i < randomLength; i++) {
|
|
48696
|
+
const randomLetter = String.fromCharCode(97 + Math.floor(Math.random() * 26));
|
|
48697
|
+
randomString += randomLetter;
|
|
48698
|
+
}
|
|
48699
|
+
return randomString;
|
|
48700
|
+
}
|
|
48701
|
+
function buildRandomPhrase() {
|
|
48702
|
+
const phrases = [];
|
|
48703
|
+
const randomLength = Math.floor(Math.random() * 10) + 5;
|
|
48704
|
+
for (let i = 0; i < randomLength; i++) {
|
|
48705
|
+
phrases.push(buildRandomString());
|
|
48706
|
+
}
|
|
48707
|
+
return phrases.join(" ");
|
|
48708
|
+
}
|
|
48709
|
+
function buildTestData(testObject) {
|
|
48710
|
+
return `{"data":{"assessmentItem":{"__typename":"AssessmentItemOrError","error":null,"item":{"__typename":"AssessmentItem","id":"x890b3c70f3e8f4a6","itemData":"${testObject}","problemType":"Type 1","sha":"c7284a3ad65214b4e62bccce236d92f7f5d35941"}}}}`;
|
|
48585
48711
|
}
|
|
48586
48712
|
|
|
48587
48713
|
const genericPerseusItemData = {
|
|
@@ -48845,6 +48971,7 @@ exports.getAngleCoords = getAngleCoords;
|
|
|
48845
48971
|
exports.getAnswerFromUserInput = getAnswerFromUserInput;
|
|
48846
48972
|
exports.getAnswersFromWidgets = getAnswersFromWidgets;
|
|
48847
48973
|
exports.getCircleCoords = getCircleCoords;
|
|
48974
|
+
exports.getClockwiseAngle = getClockwiseAngle;
|
|
48848
48975
|
exports.getCorrectAnswerForWidgetId = getCorrectAnswerForWidgetId;
|
|
48849
48976
|
exports.getImagesWithoutAltData = getImagesWithoutAltData;
|
|
48850
48977
|
exports.getLineCoords = getLineCoords;
|