@khanacademy/perseus 6.2.1 → 6.3.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/CHANGELOG.md +22 -0
- package/dist/all-widgets.d.ts +2 -2
- package/dist/all-widgets.js.flow +2 -2
- package/dist/basic-widgets.d.ts +2 -2
- package/dist/basic-widgets.js.flow +2 -2
- package/dist/components/math-input.d.ts +4 -1
- package/dist/components/math-input.js.flow +4 -1
- package/dist/components/tex-buttons.d.ts +5 -1
- package/dist/components/tex-buttons.js.flow +1 -1
- package/dist/es/index.js +172 -205
- package/dist/es/index.js.map +1 -1
- package/dist/extra-widgets.d.ts +2 -2
- package/dist/extra-widgets.js.flow +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +170 -201
- package/dist/index.js.flow +2 -0
- package/dist/index.js.map +1 -1
- package/dist/strings.js +23 -0
- package/dist/types.d.ts +2 -2
- package/dist/types.js.flow +2 -2
- package/dist/util/geometry.d.ts +25 -0
- package/dist/util/geometry.js.flow +37 -0
- package/dist/widgets/categorizer.d.ts +2 -2
- package/dist/widgets/categorizer.js.flow +2 -2
- package/dist/widgets/cs-program.d.ts +2 -2
- package/dist/widgets/cs-program.js.flow +2 -2
- package/dist/widgets/definition.d.ts +2 -2
- package/dist/widgets/definition.js.flow +2 -2
- package/dist/widgets/dropdown.d.ts +2 -2
- package/dist/widgets/dropdown.js.flow +2 -2
- package/dist/widgets/explanation.d.ts +2 -2
- package/dist/widgets/explanation.js.flow +2 -2
- package/dist/widgets/expression.d.ts +2 -2
- package/dist/widgets/expression.js.flow +2 -2
- package/dist/widgets/graded-group-set.d.ts +2 -2
- package/dist/widgets/graded-group-set.js.flow +2 -2
- package/dist/widgets/graded-group.d.ts +2 -2
- package/dist/widgets/graded-group.js.flow +2 -2
- package/dist/widgets/grapher.d.ts +2 -2
- package/dist/widgets/grapher.js.flow +2 -2
- package/dist/widgets/group.d.ts +2 -2
- package/dist/widgets/group.js.flow +2 -2
- package/dist/widgets/iframe.d.ts +2 -2
- package/dist/widgets/iframe.js.flow +2 -2
- package/dist/widgets/image.d.ts +2 -2
- package/dist/widgets/image.js.flow +2 -2
- package/dist/widgets/input-number.d.ts +2 -2
- package/dist/widgets/input-number.js.flow +2 -2
- package/dist/widgets/interaction.d.ts +2 -2
- package/dist/widgets/interaction.js.flow +2 -2
- package/dist/widgets/interactive-graph.d.ts +4 -12
- package/dist/widgets/interactive-graph.js.flow +4 -7
- package/dist/widgets/label-image.d.ts +2 -2
- package/dist/widgets/label-image.js.flow +2 -2
- package/dist/widgets/lights-puzzle.d.ts +2 -2
- package/dist/widgets/lights-puzzle.js.flow +2 -2
- package/dist/widgets/matcher.d.ts +2 -2
- package/dist/widgets/matcher.js.flow +2 -2
- package/dist/widgets/matrix.d.ts +2 -2
- package/dist/widgets/matrix.js.flow +2 -2
- package/dist/widgets/measurer.d.ts +2 -2
- package/dist/widgets/measurer.js.flow +2 -2
- package/dist/widgets/molecule.d.ts +2 -2
- package/dist/widgets/molecule.js.flow +2 -2
- package/dist/widgets/number-line.d.ts +2 -2
- package/dist/widgets/number-line.js.flow +2 -2
- package/dist/widgets/numeric-input.d.ts +2 -2
- package/dist/widgets/numeric-input.js.flow +2 -2
- package/dist/widgets/orderer.d.ts +2 -2
- package/dist/widgets/orderer.js.flow +2 -2
- package/dist/widgets/passage-ref-target.d.ts +2 -2
- package/dist/widgets/passage-ref-target.js.flow +2 -2
- package/dist/widgets/passage-ref.d.ts +2 -2
- package/dist/widgets/passage-ref.js.flow +2 -2
- package/dist/widgets/passage.d.ts +2 -2
- package/dist/widgets/passage.js.flow +2 -2
- package/dist/widgets/plotter.d.ts +2 -2
- package/dist/widgets/plotter.js.flow +2 -2
- package/dist/widgets/radio.d.ts +2 -2
- package/dist/widgets/radio.js.flow +2 -2
- package/dist/widgets/reaction-diagram.d.ts +2 -2
- package/dist/widgets/reaction-diagram.js.flow +2 -2
- package/dist/widgets/sequence.d.ts +2 -2
- package/dist/widgets/sequence.js.flow +2 -2
- package/dist/widgets/simulator.d.ts +2 -2
- package/dist/widgets/simulator.js.flow +2 -2
- package/dist/widgets/sorter.d.ts +2 -2
- package/dist/widgets/sorter.js.flow +2 -2
- package/dist/widgets/table.d.ts +2 -2
- package/dist/widgets/table.js.flow +2 -2
- package/dist/widgets/transformer.d.ts +2 -2
- package/dist/widgets/transformer.js.flow +2 -2
- package/dist/widgets/unit.d.ts +2 -2
- package/dist/widgets/unit.js.flow +2 -2
- package/dist/widgets/video.d.ts +2 -2
- package/dist/widgets/video.js.flow +2 -2
- package/package.json +4 -5
- package/src/components/__tests__/math-input.test.tsx +60 -0
- package/src/components/math-input.tsx +88 -118
- package/src/components/prop-check-box.tsx +0 -1
- package/src/components/tex-buttons.tsx +73 -12
- package/src/index.ts +2 -0
- package/src/types.ts +19 -2
- package/src/util/geometry.ts +293 -0
- package/src/widgets/__tests__/numeric-input.test.ts +38 -0
- package/src/widgets/expression.tsx +0 -1
- package/src/widgets/grapher.tsx +0 -1
- package/src/widgets/image.tsx +0 -1
- package/src/widgets/input-number.tsx +0 -1
- package/src/widgets/interactive-graph.tsx +20 -288
- package/src/widgets/matrix.tsx +0 -1
- package/src/widgets/numeric-input.tsx +6 -2
- package/src/widgets/transformer.tsx +0 -1
- package/tsconfig-build.json +0 -1
- package/tsconfig-build.tsbuildinfo +1 -1
- package/dist/__tests__/mock-asset-loading-widget.d.ts +0 -59
- package/dist/__tests__/mock-asset-loading-widget.js.flow +0 -70
- package/dist/__tests__/mock-widget.d.ts +0 -30
- package/dist/__tests__/mock-widget.js.flow +0 -39
- package/dist/__tests__/test-items/image-item.d.ts +0 -14
- package/dist/__tests__/test-items/image-item.js.flow +0 -20
- package/dist/__tests__/test-items/input-number-1-item.d.ts +0 -9
- package/dist/__tests__/test-items/input-number-1-item.js.flow +0 -15
- package/dist/__tests__/test-items/input-number-2-item.d.ts +0 -9
- package/dist/__tests__/test-items/input-number-2-item.js.flow +0 -15
- package/dist/__tests__/test-items/table-item.d.ts +0 -9
- package/dist/__tests__/test-items/table-item.js.flow +0 -15
- package/dist/interactive2/__tests__/movable-mock.d.ts +0 -5
- package/dist/interactive2/__tests__/movable-mock.js.flow +0 -11
- package/dist/widgets/__stories__/test-keypad-context-wrapper.d.ts +0 -6
- package/dist/widgets/__stories__/test-keypad-context-wrapper.js.flow +0 -12
- package/dist/widgets/__tests__/renderQuestion.d.ts +0 -13
- package/dist/widgets/__tests__/renderQuestion.js.flow +0 -28
|
@@ -13,10 +13,28 @@ import {Errors} from "../logging/log";
|
|
|
13
13
|
import {PerseusError} from "../perseus-error";
|
|
14
14
|
import Util from "../util";
|
|
15
15
|
import KhanColors from "../util/colors";
|
|
16
|
+
import {
|
|
17
|
+
QuadraticCoefficient,
|
|
18
|
+
SineCoefficient,
|
|
19
|
+
Range,
|
|
20
|
+
angleMeasures,
|
|
21
|
+
ccw,
|
|
22
|
+
collinear,
|
|
23
|
+
intersects,
|
|
24
|
+
lawOfCosines,
|
|
25
|
+
magnitude,
|
|
26
|
+
rotate,
|
|
27
|
+
vector,
|
|
28
|
+
sign,
|
|
29
|
+
getLineEquation,
|
|
30
|
+
getLineIntersection,
|
|
31
|
+
canonicalSineCoefficients,
|
|
32
|
+
similar,
|
|
33
|
+
} from "../util/geometry";
|
|
16
34
|
import GraphUtils from "../util/graph-utils";
|
|
17
35
|
import {getInteractiveBoxFromSizeClass} from "../util/sizing-utils";
|
|
18
36
|
|
|
19
|
-
import type {Coord
|
|
37
|
+
import type {Coord} from "../interactive2/types";
|
|
20
38
|
import type {
|
|
21
39
|
PerseusGraphType,
|
|
22
40
|
PerseusGraphTypeAngle,
|
|
@@ -27,15 +45,6 @@ import type {
|
|
|
27
45
|
} from "../perseus-types";
|
|
28
46
|
import type {PerseusScore, WidgetExports, WidgetProps} from "../types";
|
|
29
47
|
|
|
30
|
-
type Range = ReadonlyArray<number>; // This should really be a readonly tuple of [number, number]
|
|
31
|
-
type SineCoefficient = [
|
|
32
|
-
/* amplitude */ number,
|
|
33
|
-
/* angularFrequency */ number,
|
|
34
|
-
/* phase */ number,
|
|
35
|
-
/* verticalOffset */ number,
|
|
36
|
-
];
|
|
37
|
-
type QuadraticCoefficient = [/* a */ number, /* b */ number, /* c */ number];
|
|
38
|
-
|
|
39
48
|
const {DeprecationMixin} = Util;
|
|
40
49
|
|
|
41
50
|
const TRASH_ICON_URI =
|
|
@@ -53,289 +62,22 @@ const UNLIMITED = "unlimited" as const;
|
|
|
53
62
|
// Sample background image:
|
|
54
63
|
// https://ka-perseus-graphie.s3.amazonaws.com/29c1b0fcd17fe63df0f148fe357044d5d5c7d0bb.png
|
|
55
64
|
|
|
56
|
-
function ccw(a: Coord, b: Coord, c: Coord): number {
|
|
57
|
-
return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function collinear(a: Coord, b: Coord, c: Coord): boolean {
|
|
61
|
-
return eq(ccw(a, b, c), 0);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function sign(val: number): 0 | 1 | -1 {
|
|
65
|
-
if (eq(val, 0)) {
|
|
66
|
-
return 0;
|
|
67
|
-
}
|
|
68
|
-
return val > 0 ? 1 : -1;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
65
|
// default to defaultValue if actual is null or undefined
|
|
72
66
|
function defaultVal<T>(actual: T | null | undefined, defaultValue: T): T {
|
|
73
67
|
return actual == null ? defaultValue : actual;
|
|
74
68
|
}
|
|
75
69
|
|
|
76
|
-
// Given rect bounding points A and B, whether point C is inside the rect
|
|
77
|
-
function pointInRect(a: Coord, b: Coord, c: Coord): boolean {
|
|
78
|
-
return (
|
|
79
|
-
c[0] <= Math.max(a[0], b[0]) &&
|
|
80
|
-
c[0] >= Math.min(a[0], b[0]) &&
|
|
81
|
-
c[1] <= Math.max(a[1], b[1]) &&
|
|
82
|
-
c[1] >= Math.min(a[1], b[1])
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Whether line segment AB intersects line segment CD
|
|
87
|
-
// http://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
|
|
88
|
-
function intersects(ab: Line, cd: Line): boolean {
|
|
89
|
-
const triplets = [
|
|
90
|
-
[ab[0], ab[1], cd[0]],
|
|
91
|
-
[ab[0], ab[1], cd[1]],
|
|
92
|
-
[cd[0], cd[1], ab[0]],
|
|
93
|
-
[cd[0], cd[1], ab[1]],
|
|
94
|
-
];
|
|
95
|
-
|
|
96
|
-
const orientations = _.map(triplets, function (triplet) {
|
|
97
|
-
// @ts-expect-error [FEI-5003] - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
98
|
-
return sign(ccw(...triplet));
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
if (
|
|
102
|
-
orientations[0] !== orientations[1] &&
|
|
103
|
-
orientations[2] !== orientations[3]
|
|
104
|
-
) {
|
|
105
|
-
return true;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
for (let i = 0; i < 4; i++) {
|
|
109
|
-
// @ts-expect-error [FEI-5003] - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
110
|
-
if (orientations[i] === 0 && pointInRect(...triplets[i])) {
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function vector(a, b) {
|
|
119
|
-
return _.map(_.zip(a, b), function (pair) {
|
|
120
|
-
return pair[0] - pair[1];
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function magnitude(v: ReadonlyArray<Coord>): number {
|
|
125
|
-
return Math.sqrt(
|
|
126
|
-
_.reduce(
|
|
127
|
-
v,
|
|
128
|
-
function (memo, el) {
|
|
129
|
-
// @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'Coord' is not assignable to parameter of type 'number'.
|
|
130
|
-
return memo + Math.pow(el, 2);
|
|
131
|
-
},
|
|
132
|
-
0,
|
|
133
|
-
),
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function dotProduct(a: Coord, b: Coord): number {
|
|
138
|
-
return _.reduce(
|
|
139
|
-
_.zip(a, b),
|
|
140
|
-
function (memo, pair) {
|
|
141
|
-
return memo + pair[0] * pair[1];
|
|
142
|
-
},
|
|
143
|
-
0,
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function sideLengths(coords: ReadonlyArray<Coord>): ReadonlyArray<number> {
|
|
148
|
-
const segments = _.zip(coords, rotate(coords));
|
|
149
|
-
return segments.map(function (segment) {
|
|
150
|
-
// @ts-expect-error [FEI-5003] - 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.
|
|
151
|
-
return magnitude(vector(...segment));
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Based on http://math.stackexchange.com/a/151149
|
|
156
|
-
function angleMeasures(coords: ReadonlyArray<Coord>): ReadonlyArray<number> {
|
|
157
|
-
const triplets = _.zip(rotate(coords, -1), coords, rotate(coords, 1));
|
|
158
|
-
|
|
159
|
-
const offsets = _.map(triplets, function (triplet) {
|
|
160
|
-
const p = vector(triplet[1], triplet[0]);
|
|
161
|
-
const q = vector(triplet[2], triplet[1]);
|
|
162
|
-
// @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'Coord'. | TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
163
|
-
const raw = Math.acos(dotProduct(p, q) / (magnitude(p) * magnitude(q)));
|
|
164
|
-
// @ts-expect-error [FEI-5003] - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
165
|
-
return sign(ccw(...triplet)) > 0 ? raw : -raw;
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
const sum = _.reduce(
|
|
169
|
-
offsets,
|
|
170
|
-
function (memo, arg) {
|
|
171
|
-
return memo + arg;
|
|
172
|
-
},
|
|
173
|
-
0,
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
return _.map(offsets, function (offset) {
|
|
177
|
-
return sum > 0 ? Math.PI - offset : Math.PI + offset;
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Whether two polygons are similar (or if specified, congruent)
|
|
182
|
-
function similar(
|
|
183
|
-
coords1: ReadonlyArray<Coord>,
|
|
184
|
-
coords2: ReadonlyArray<Coord>,
|
|
185
|
-
tolerance: number,
|
|
186
|
-
): boolean {
|
|
187
|
-
if (coords1.length !== coords2.length) {
|
|
188
|
-
return false;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const n = coords1.length;
|
|
192
|
-
|
|
193
|
-
const angles1 = angleMeasures(coords1);
|
|
194
|
-
const angles2 = angleMeasures(coords2);
|
|
195
|
-
|
|
196
|
-
const sides1 = sideLengths(coords1);
|
|
197
|
-
const sides2 = sideLengths(coords2);
|
|
198
|
-
|
|
199
|
-
for (let i = 0; i < 2 * n; i++) {
|
|
200
|
-
let angles = angles2.slice();
|
|
201
|
-
let sides = sides2.slice();
|
|
202
|
-
|
|
203
|
-
// Reverse angles and sides to allow matching reflected polygons
|
|
204
|
-
if (i >= n) {
|
|
205
|
-
angles.reverse();
|
|
206
|
-
sides.reverse();
|
|
207
|
-
// Since sides are calculated from two coordinates,
|
|
208
|
-
// simply reversing results in an off by one error
|
|
209
|
-
// @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
210
|
-
sides = rotate(sides, 1);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
214
|
-
angles = rotate(angles, i);
|
|
215
|
-
// @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
216
|
-
sides = rotate(sides, i);
|
|
217
|
-
|
|
218
|
-
if (deepEq(angles1, angles)) {
|
|
219
|
-
const sidePairs = _.zip(sides1, sides);
|
|
220
|
-
|
|
221
|
-
const factors = _.map(sidePairs, function (pair) {
|
|
222
|
-
return pair[0] / pair[1];
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
const same = _.all(factors, function (factor) {
|
|
226
|
-
return eq(factors[0], factor);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
const congruentEnough = _.all(sidePairs, function (pair) {
|
|
230
|
-
return knumber.equal(pair[0], pair[1], tolerance);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
if (same && congruentEnough) {
|
|
234
|
-
return true;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return false;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
70
|
// Less than or approximately equal
|
|
243
71
|
function leq(a: any, b) {
|
|
244
72
|
return a < b || eq(a, b);
|
|
245
73
|
}
|
|
246
74
|
|
|
247
|
-
// Given triangle with sides ABC return angle opposite side C in degrees
|
|
248
|
-
function lawOfCosines(a: number, b: number, c: number): number {
|
|
249
|
-
return (Math.acos((a * a + b * b - c * c) / (2 * a * b)) * 180) / Math.PI;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function canonicalSineCoefficients([
|
|
253
|
-
amplitude,
|
|
254
|
-
angularFrequency,
|
|
255
|
-
phase,
|
|
256
|
-
verticalOffset,
|
|
257
|
-
]: [any, any, any, any]) {
|
|
258
|
-
// For a curve of the form f(x) = a * Sin(b * x - c) + d,
|
|
259
|
-
// this function ensures that a, b > 0, and c is its
|
|
260
|
-
// smallest possible positive value.
|
|
261
|
-
|
|
262
|
-
// Guarantee a > 0
|
|
263
|
-
if (amplitude < 0) {
|
|
264
|
-
amplitude *= -1;
|
|
265
|
-
angularFrequency *= -1;
|
|
266
|
-
phase *= -1;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const period = 2 * Math.PI;
|
|
270
|
-
// Guarantee b > 0
|
|
271
|
-
if (angularFrequency < 0) {
|
|
272
|
-
angularFrequency *= -1;
|
|
273
|
-
phase *= -1;
|
|
274
|
-
phase += period / 2;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Guarantee c is smallest possible positive value
|
|
278
|
-
while (phase > 0) {
|
|
279
|
-
phase -= period;
|
|
280
|
-
}
|
|
281
|
-
while (phase < 0) {
|
|
282
|
-
phase += period;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return [amplitude, angularFrequency, phase, verticalOffset];
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// e.g. rotate([1, 2, 3]) -> [2, 3, 1]
|
|
289
|
-
function rotate<T>(array: ReadonlyArray<T>, n?: number): ReadonlyArray<T> {
|
|
290
|
-
n = typeof n === "undefined" ? 1 : n % array.length;
|
|
291
|
-
return array.slice(n).concat(array.slice(0, n));
|
|
292
|
-
}
|
|
293
|
-
|
|
294
75
|
function capitalize(str) {
|
|
295
76
|
return str.replace(/(?:^|-)(.)/g, function (match, letter) {
|
|
296
77
|
return letter.toUpperCase();
|
|
297
78
|
});
|
|
298
79
|
}
|
|
299
80
|
|
|
300
|
-
function getLineEquation(first: Coord, second: Coord): string {
|
|
301
|
-
if (eq(first[0], second[0])) {
|
|
302
|
-
return "x = " + first[0].toFixed(3);
|
|
303
|
-
}
|
|
304
|
-
const m = (second[1] - first[1]) / (second[0] - first[0]);
|
|
305
|
-
const b = first[1] - m * first[0];
|
|
306
|
-
return "y = " + m.toFixed(3) + "x + " + b.toFixed(3);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Stolen from the wikipedia article
|
|
310
|
-
// http://en.wikipedia.org/wiki/Line-line_intersection
|
|
311
|
-
function getLineIntersection(
|
|
312
|
-
// TODO(LP-10725): update these to be 2-tuples
|
|
313
|
-
firstPoints: ReadonlyArray<Coord>,
|
|
314
|
-
secondPoints: ReadonlyArray<Coord>,
|
|
315
|
-
): string {
|
|
316
|
-
const x1 = firstPoints[0][0];
|
|
317
|
-
const y1 = firstPoints[0][1];
|
|
318
|
-
const x2 = firstPoints[1][0];
|
|
319
|
-
const y2 = firstPoints[1][1];
|
|
320
|
-
const x3 = secondPoints[0][0];
|
|
321
|
-
const y3 = secondPoints[0][1];
|
|
322
|
-
const x4 = secondPoints[1][0];
|
|
323
|
-
const y4 = secondPoints[1][1];
|
|
324
|
-
|
|
325
|
-
const determinant = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
|
326
|
-
|
|
327
|
-
if (Math.abs(determinant) < 1e-9) {
|
|
328
|
-
return "Lines are parallel";
|
|
329
|
-
}
|
|
330
|
-
const x =
|
|
331
|
-
((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) /
|
|
332
|
-
determinant;
|
|
333
|
-
const y =
|
|
334
|
-
((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) /
|
|
335
|
-
determinant;
|
|
336
|
-
return "Intersection: (" + x.toFixed(3) + ", " + y.toFixed(3) + ")";
|
|
337
|
-
}
|
|
338
|
-
|
|
339
81
|
function numSteps(range: Range, step: number) {
|
|
340
82
|
return Math.floor((range[1] - range[0]) / step);
|
|
341
83
|
}
|
|
@@ -1937,16 +1679,6 @@ class InteractiveGraph extends React.Component<Props, State> {
|
|
|
1937
1679
|
this.angle?.remove();
|
|
1938
1680
|
};
|
|
1939
1681
|
|
|
1940
|
-
toggleShowAngles: () => void = () => {
|
|
1941
|
-
if (this.props.graph.type !== "polygon") {
|
|
1942
|
-
throw makeInvalidTypeError("toggleShowAngles", "polygon");
|
|
1943
|
-
}
|
|
1944
|
-
const graph = _.extend({}, this.props.graph, {
|
|
1945
|
-
showAngles: !this.props.graph.showAngles,
|
|
1946
|
-
});
|
|
1947
|
-
this.onChange({graph: graph});
|
|
1948
|
-
};
|
|
1949
|
-
|
|
1950
1682
|
toggleShowSides: () => void = () => {
|
|
1951
1683
|
if (this.props.graph.type !== "polygon") {
|
|
1952
1684
|
throw makeInvalidTypeError("toggleShowSides", "polygon");
|
|
@@ -1961,7 +1693,7 @@ class InteractiveGraph extends React.Component<Props, State> {
|
|
|
1961
1693
|
return InteractiveGraph.getUserInputFromProps(this.props);
|
|
1962
1694
|
};
|
|
1963
1695
|
|
|
1964
|
-
simpleValidate: (
|
|
1696
|
+
simpleValidate: (rubric: Rubric) => PerseusScore = (rubric) => {
|
|
1965
1697
|
return InteractiveGraph.validate(this.getUserInput(), rubric, this);
|
|
1966
1698
|
};
|
|
1967
1699
|
|
package/src/widgets/matrix.tsx
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable react/prop-types */
|
|
2
1
|
import {linterContextDefault} from "@khanacademy/perseus-linter";
|
|
3
2
|
import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
4
3
|
import {StyleSheet} from "aphrodite";
|
|
@@ -151,7 +150,12 @@ export class NumericInput extends React.Component<Props, State> {
|
|
|
151
150
|
}
|
|
152
151
|
|
|
153
152
|
static validate(useInput: UserInput, rubric: Rubric): PerseusScore {
|
|
154
|
-
const allAnswerForms = answerFormButtons
|
|
153
|
+
const allAnswerForms = answerFormButtons
|
|
154
|
+
.map((e) => e["value"])
|
|
155
|
+
// Don't default to validating the answer as a pi answer
|
|
156
|
+
// if answerForm isn't set on the answer
|
|
157
|
+
// https://khanacademy.atlassian.net/browse/LC-691
|
|
158
|
+
.filter((e) => e !== "pi");
|
|
155
159
|
|
|
156
160
|
const createValidator = (answer: PerseusNumericInputAnswer) => {
|
|
157
161
|
const stringAnswer = `${answer.value}`;
|