@khanacademy/perseus 13.1.0 → 14.0.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 +43 -0
- package/dist/components/svg-image.d.ts +10 -9
- package/dist/es/index.css +0 -7
- package/dist/es/index.js +37 -31
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +0 -7
- package/dist/index.js +61 -49
- package/dist/index.js.map +1 -1
- package/dist/renderer.d.ts +2 -2
- package/dist/widgets/matcher.d.ts +1 -1
- package/package.json +35 -35
- package/src/__stories__/hints-renderer.stories.tsx +41 -44
- package/src/__tests__/__snapshots__/renderer.test.tsx.snap +9 -33
- package/src/__tests__/article-renderer.test.tsx +2 -3
- package/src/__tests__/renderer-api.test.tsx +35 -52
- package/src/components/graph.tsx +14 -8
- package/src/components/svg-image.tsx +13 -11
- package/src/mixins/provide-keypad.tsx +0 -1
- package/src/perseus-types.ts +8 -3
- package/src/renderer.tsx +2 -2
- package/src/styles/khan-exercise.css +0 -2
- package/src/styles/perseus-renderer.less +0 -7
- package/src/widgets/__stories__/random-widgets.stories.tsx +59 -0
- package/src/widgets/__stories__/test-keypad-context-wrapper.tsx +0 -1
- package/src/widgets/__testdata__/categorizer.testdata.ts +40 -0
- package/src/widgets/__testdata__/dropdown.testdata.ts +38 -0
- package/src/widgets/__testdata__/explanation.testdata.ts +30 -0
- package/src/widgets/__testdata__/expression.testdata.ts +82 -0
- package/src/widgets/__testdata__/interactive-graph-random.testdata.ts +269 -0
- package/src/widgets/__testdata__/radio.testdata.ts +63 -0
- package/src/widgets/__testdata__/randomizers.ts +51 -0
- package/src/widgets/__tests__/__snapshots__/dropdown.test.ts.snap +6 -22
- package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt.test.ts.snap +3 -3
- package/src/widgets/__tests__/__snapshots__/graded-group-set.test.ts.snap +1 -1
- package/src/widgets/__tests__/__snapshots__/graded-group.test.ts.snap +1 -1
- package/src/widgets/__tests__/__snapshots__/radio.test.ts.snap +4 -4
- package/src/widgets/__tests__/expression-mobile.test.tsx +0 -1
- package/src/widgets/__tests__/matcher.test.tsx +1 -1
- package/src/widgets/__tests__/randomizers.test.ts +53 -0
- package/src/widgets/image.tsx +27 -20
- package/src/widgets/matcher.tsx +1 -1
- package/src/widgets/radio/choice.tsx +1 -0
- package/tsconfig-build.tsbuildinfo +1 -1
package/src/perseus-types.ts
CHANGED
|
@@ -534,7 +534,7 @@ export type PerseusInteractiveGraphWidgetOptions = {
|
|
|
534
534
|
showTooltips?: boolean;
|
|
535
535
|
// The unit to show on the ruler. e.g. "mm", "cm", "m", "km", "in", "ft", "yd", "mi"
|
|
536
536
|
rulerLabel: string;
|
|
537
|
-
// How many ticks to show on the ruler. e.g. 1, 2, 4, 8, 10, 16
|
|
537
|
+
// How many ticks to show on the ruler. e.g. 1, 2, 4, 8, 10, 16. Must be an integer.
|
|
538
538
|
rulerTicks: number;
|
|
539
539
|
// The X and Y coordinate ranges for the view of the graph. default: [[-10, 10], [-10, 10]]
|
|
540
540
|
// NOTE(kevinb): perseus_data.go defines this as Array<Array<number>>
|
|
@@ -577,6 +577,7 @@ export type PerseusGraphTypeAngle = {
|
|
|
577
577
|
snapDegrees?: number;
|
|
578
578
|
// How to match the answer. If missing, defaults to exact matching.
|
|
579
579
|
match?: "congruent";
|
|
580
|
+
// must have 3 coords - ie [Coord, Coord, Coord]
|
|
580
581
|
coords?: ReadonlyArray<Coord>;
|
|
581
582
|
};
|
|
582
583
|
|
|
@@ -588,11 +589,13 @@ export type PerseusGraphTypeCircle = {
|
|
|
588
589
|
|
|
589
590
|
export type PerseusGraphTypeLinear = {
|
|
590
591
|
type: "linear";
|
|
592
|
+
// expects 2 coords
|
|
591
593
|
coords?: ReadonlyArray<Coord>;
|
|
592
594
|
} & PerseusGraphTypeCommon;
|
|
593
595
|
|
|
594
596
|
export type PerseusGraphTypeLinearSystem = {
|
|
595
597
|
type: "linear-system";
|
|
598
|
+
// expects 2 sets of 2 coords
|
|
596
599
|
coords?: ReadonlyArray<ReadonlyArray<Coord>>;
|
|
597
600
|
} & PerseusGraphTypeCommon;
|
|
598
601
|
|
|
@@ -620,6 +623,7 @@ export type PerseusGraphTypePolygon = {
|
|
|
620
623
|
|
|
621
624
|
export type PerseusGraphTypeQuadratic = {
|
|
622
625
|
type: "quadratic";
|
|
626
|
+
// expects a list of 3 coords
|
|
623
627
|
coords?: ReadonlyArray<Coord>;
|
|
624
628
|
} & PerseusGraphTypeCommon;
|
|
625
629
|
|
|
@@ -627,18 +631,19 @@ export type PerseusGraphTypeSegment = {
|
|
|
627
631
|
type: "segment";
|
|
628
632
|
// The number of segments if a "segment" type. default: 1. Max: 6
|
|
629
633
|
numSegments?: number;
|
|
630
|
-
//
|
|
631
|
-
// Length should match the `numSegments` value.
|
|
634
|
+
// Expects a list of Coord tuples. Length should match the `numSegments` value.
|
|
632
635
|
coords?: ReadonlyArray<ReadonlyArray<Coord>>;
|
|
633
636
|
} & PerseusGraphTypeCommon;
|
|
634
637
|
|
|
635
638
|
export type PerseusGraphTypeSinusoid = {
|
|
636
639
|
type: "sinusoid";
|
|
640
|
+
// Expects a list of 2 Coords
|
|
637
641
|
coords?: ReadonlyArray<Coord>;
|
|
638
642
|
} & PerseusGraphTypeCommon;
|
|
639
643
|
|
|
640
644
|
export type PerseusGraphTypeRay = {
|
|
641
645
|
type: "ray";
|
|
646
|
+
// Expects a list of 2 Coords
|
|
642
647
|
coords?: ReadonlyArray<Coord>;
|
|
643
648
|
} & PerseusGraphTypeCommon;
|
|
644
649
|
|
package/src/renderer.tsx
CHANGED
|
@@ -132,7 +132,7 @@ export type Widget = {
|
|
|
132
132
|
path: FocusPath,
|
|
133
133
|
newValue: string,
|
|
134
134
|
// TODO(jeremy): I think this is actually a callback
|
|
135
|
-
focus
|
|
135
|
+
focus?: () => unknown,
|
|
136
136
|
) => void;
|
|
137
137
|
getUserInput?: () => WidgetUserInput | null | undefined;
|
|
138
138
|
simpleValidate?: (
|
|
@@ -1701,7 +1701,7 @@ class Renderer extends React.Component<Props, State> {
|
|
|
1701
1701
|
setInputValue: (
|
|
1702
1702
|
path: FocusPath,
|
|
1703
1703
|
newValue: string,
|
|
1704
|
-
focus
|
|
1704
|
+
focus?: () => unknown,
|
|
1705
1705
|
) => void = (path, newValue, focus) => {
|
|
1706
1706
|
// @ts-expect-error - TS2345 - Argument of type 'FocusPath' is not assignable to parameter of type 'List<any>'.
|
|
1707
1707
|
const widgetId = _.first(path);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import Button from "@khanacademy/wonder-blocks-button";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import {RendererWithDebugUI} from "../../../../../testing/renderer-with-debug-ui";
|
|
5
|
+
import {randomCategorizerGenerator} from "../__testdata__/categorizer.testdata";
|
|
6
|
+
import {randomDropdownGenerator} from "../__testdata__/dropdown.testdata";
|
|
7
|
+
import {randomExplanationGenerator} from "../__testdata__/explanation.testdata";
|
|
8
|
+
import {randomExpressionGenerator} from "../__testdata__/expression.testdata";
|
|
9
|
+
import {randomInteractiveGraphGenerator} from "../__testdata__/interactive-graph-random.testdata";
|
|
10
|
+
import {randomRadioGenerator} from "../__testdata__/radio.testdata";
|
|
11
|
+
|
|
12
|
+
import type {PerseusRenderer} from "@khanacademy/perseus";
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
title: "Perseus/Randomized Widgets",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const RandomizedWidgetContainer = (
|
|
19
|
+
randomizer: () => PerseusRenderer,
|
|
20
|
+
): React.ReactElement => {
|
|
21
|
+
const [question, setQuestion] = React.useState(randomizer());
|
|
22
|
+
const randomize = () => {
|
|
23
|
+
setQuestion(randomizer());
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div>
|
|
28
|
+
<Button size="small" style={{marginBottom: 24}} onClick={randomize}>
|
|
29
|
+
Randomize
|
|
30
|
+
</Button>
|
|
31
|
+
|
|
32
|
+
<RendererWithDebugUI question={question} />
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const RandomCategorizer = (): React.ReactElement => {
|
|
38
|
+
return RandomizedWidgetContainer(randomCategorizerGenerator);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const RandomDropdown = (): React.ReactElement => {
|
|
42
|
+
return RandomizedWidgetContainer(randomDropdownGenerator);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const RandomExplanation = (): React.ReactElement => {
|
|
46
|
+
return RandomizedWidgetContainer(randomExplanationGenerator);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const RandomExpression = (): React.ReactElement => {
|
|
50
|
+
return RandomizedWidgetContainer(randomExpressionGenerator);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const RandomInteractiveGraph = (): React.ReactElement => {
|
|
54
|
+
return RandomizedWidgetContainer(randomInteractiveGraphGenerator);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const RandomRadio = (): React.ReactElement => {
|
|
58
|
+
return RandomizedWidgetContainer(randomRadioGenerator);
|
|
59
|
+
};
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
arrayOfLength,
|
|
3
|
+
randomBoolean,
|
|
4
|
+
randomInteger,
|
|
5
|
+
randomSentence,
|
|
6
|
+
} from "./randomizers";
|
|
7
|
+
|
|
1
8
|
import type {PerseusRenderer} from "../../perseus-types";
|
|
2
9
|
|
|
3
10
|
export const question1: PerseusRenderer = {
|
|
@@ -47,3 +54,36 @@ export const question1: PerseusRenderer = {
|
|
|
47
54
|
},
|
|
48
55
|
},
|
|
49
56
|
};
|
|
57
|
+
|
|
58
|
+
export const randomCategorizerGenerator = (): PerseusRenderer => {
|
|
59
|
+
const questionText = randomSentence(35);
|
|
60
|
+
const numItems = randomInteger(2, 7);
|
|
61
|
+
const numCategories = randomInteger(2, 6);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
content: `${questionText}\n\n\n[[\u2603 categorizer 1]]`,
|
|
65
|
+
images: {},
|
|
66
|
+
widgets: {
|
|
67
|
+
"categorizer 1": {
|
|
68
|
+
version: {major: 0, minor: 0},
|
|
69
|
+
type: "categorizer",
|
|
70
|
+
graded: randomBoolean(),
|
|
71
|
+
alignment: "default",
|
|
72
|
+
options: {
|
|
73
|
+
items: arrayOfLength(numItems).map(() =>
|
|
74
|
+
randomSentence(12),
|
|
75
|
+
),
|
|
76
|
+
values: arrayOfLength(numItems).map(() =>
|
|
77
|
+
randomInteger(0, numCategories - 1),
|
|
78
|
+
),
|
|
79
|
+
randomizeItems: randomBoolean(),
|
|
80
|
+
categories: arrayOfLength(numCategories).map(() =>
|
|
81
|
+
randomSentence(7),
|
|
82
|
+
),
|
|
83
|
+
highlightLint: randomBoolean(),
|
|
84
|
+
static: randomBoolean(0.05),
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
};
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
arrayOfLength,
|
|
3
|
+
randomBoolean,
|
|
4
|
+
randomInteger,
|
|
5
|
+
randomSentence,
|
|
6
|
+
} from "./randomizers";
|
|
7
|
+
|
|
1
8
|
import type {PerseusRenderer} from "../../perseus-types";
|
|
2
9
|
|
|
3
10
|
export const question1: PerseusRenderer = {
|
|
@@ -31,3 +38,34 @@ export const question1: PerseusRenderer = {
|
|
|
31
38
|
},
|
|
32
39
|
},
|
|
33
40
|
};
|
|
41
|
+
|
|
42
|
+
export const randomDropdownGenerator = (): PerseusRenderer => {
|
|
43
|
+
const numChoices = randomInteger(2, 16);
|
|
44
|
+
const correctIndex = randomInteger(0, numChoices - 1);
|
|
45
|
+
return {
|
|
46
|
+
content: `${randomSentence(20)} [[☃ dropdown 1]] ${randomSentence(10)}`,
|
|
47
|
+
images: {},
|
|
48
|
+
widgets: {
|
|
49
|
+
"dropdown 1": {
|
|
50
|
+
type: "dropdown",
|
|
51
|
+
alignment: "default",
|
|
52
|
+
static: randomBoolean(0.05),
|
|
53
|
+
graded: randomBoolean(),
|
|
54
|
+
options: {
|
|
55
|
+
static: randomBoolean(0.05),
|
|
56
|
+
placeholder: randomSentence(10),
|
|
57
|
+
choices: arrayOfLength(numChoices).map((_, i) => {
|
|
58
|
+
return {
|
|
59
|
+
content: randomSentence(10),
|
|
60
|
+
correct: i === correctIndex,
|
|
61
|
+
};
|
|
62
|
+
}),
|
|
63
|
+
},
|
|
64
|
+
version: {
|
|
65
|
+
major: 0,
|
|
66
|
+
minor: 0,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {randomBoolean, randomSentence} from "./randomizers";
|
|
2
|
+
|
|
1
3
|
import type {PerseusRenderer} from "../../perseus-types";
|
|
2
4
|
|
|
3
5
|
export const question1: PerseusRenderer = {
|
|
@@ -53,3 +55,31 @@ export const question2: PerseusRenderer = {
|
|
|
53
55
|
},
|
|
54
56
|
},
|
|
55
57
|
};
|
|
58
|
+
|
|
59
|
+
export const randomExplanationGenerator = (): PerseusRenderer => {
|
|
60
|
+
return {
|
|
61
|
+
content: `${randomSentence(
|
|
62
|
+
50,
|
|
63
|
+
)}\n[[\u2603 explanation 1]]\n${randomSentence(50)}`,
|
|
64
|
+
images: {},
|
|
65
|
+
widgets: {
|
|
66
|
+
"explanation 1": {
|
|
67
|
+
graded: randomBoolean(),
|
|
68
|
+
version: {
|
|
69
|
+
major: 0,
|
|
70
|
+
minor: 0,
|
|
71
|
+
},
|
|
72
|
+
static: randomBoolean(0.05),
|
|
73
|
+
type: "explanation",
|
|
74
|
+
options: {
|
|
75
|
+
hidePrompt: `${randomSentence(20)}`,
|
|
76
|
+
widgets: {},
|
|
77
|
+
explanation: `${randomSentence(50)}`,
|
|
78
|
+
static: randomBoolean(0.05),
|
|
79
|
+
showPrompt: `${randomSentence(7)}`,
|
|
80
|
+
},
|
|
81
|
+
alignment: "default",
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
};
|
|
@@ -1,7 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
arrayOfLength,
|
|
3
|
+
randomBoolean,
|
|
4
|
+
randomElement,
|
|
5
|
+
randomInteger,
|
|
6
|
+
randomLetter,
|
|
7
|
+
randomSentence,
|
|
8
|
+
randomWord,
|
|
9
|
+
} from "./randomizers";
|
|
10
|
+
|
|
1
11
|
import type {
|
|
2
12
|
PerseusExpressionWidgetOptions,
|
|
3
13
|
Version,
|
|
4
14
|
PerseusItem,
|
|
15
|
+
PerseusRenderer,
|
|
16
|
+
ExpressionWidget,
|
|
5
17
|
} from "../../perseus-types";
|
|
6
18
|
|
|
7
19
|
const createItemJson = (
|
|
@@ -96,3 +108,73 @@ export const expressionItem3: PerseusItem = createItemJson(
|
|
|
96
108
|
minor: 0,
|
|
97
109
|
},
|
|
98
110
|
);
|
|
111
|
+
|
|
112
|
+
export const randomExpressionGenerator = (): PerseusRenderer => {
|
|
113
|
+
const randomButtonSet = [
|
|
114
|
+
"basic",
|
|
115
|
+
"basic+div",
|
|
116
|
+
"trig",
|
|
117
|
+
"prealgebra",
|
|
118
|
+
"logarithms",
|
|
119
|
+
"basic relations",
|
|
120
|
+
"advanced relations",
|
|
121
|
+
]
|
|
122
|
+
.sort(() => {
|
|
123
|
+
return randomBoolean() ? 1 : -1;
|
|
124
|
+
})
|
|
125
|
+
.slice(0, randomInteger(1, 6));
|
|
126
|
+
|
|
127
|
+
const randomFunctionSet = arrayOfLength(randomInteger(0, 6)).map(
|
|
128
|
+
randomLetter,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
content: `${randomSentence(20)} [[☃ expression 1]]`,
|
|
133
|
+
images: {},
|
|
134
|
+
widgets: {
|
|
135
|
+
"expression 1": {
|
|
136
|
+
type: "expression",
|
|
137
|
+
graded: randomBoolean(),
|
|
138
|
+
version: {
|
|
139
|
+
major: 1,
|
|
140
|
+
minor: 0,
|
|
141
|
+
},
|
|
142
|
+
static: randomBoolean(0.05),
|
|
143
|
+
options: {
|
|
144
|
+
answerForms: [
|
|
145
|
+
{
|
|
146
|
+
considered: randomElement([
|
|
147
|
+
"correct",
|
|
148
|
+
"wrong",
|
|
149
|
+
"ungraded",
|
|
150
|
+
]),
|
|
151
|
+
form: randomBoolean(),
|
|
152
|
+
simplify: randomBoolean(),
|
|
153
|
+
value: randomWord(),
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
considered: randomElement([
|
|
157
|
+
"correct",
|
|
158
|
+
"wrong",
|
|
159
|
+
"ungraded",
|
|
160
|
+
]),
|
|
161
|
+
form: randomBoolean(),
|
|
162
|
+
simplify: randomBoolean(),
|
|
163
|
+
value: randomWord(),
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
times: randomBoolean(),
|
|
167
|
+
buttonSets: randomButtonSet,
|
|
168
|
+
functions: randomFunctionSet,
|
|
169
|
+
buttonsVisible: randomElement([
|
|
170
|
+
"always",
|
|
171
|
+
"never",
|
|
172
|
+
"focused",
|
|
173
|
+
undefined,
|
|
174
|
+
]),
|
|
175
|
+
alignment: "default",
|
|
176
|
+
},
|
|
177
|
+
} as ExpressionWidget,
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
};
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import {
|
|
2
|
+
arrayOfLength,
|
|
3
|
+
randomBoolean,
|
|
4
|
+
randomElement,
|
|
5
|
+
randomInteger,
|
|
6
|
+
randomLetter,
|
|
7
|
+
randomWord,
|
|
8
|
+
} from "./randomizers";
|
|
9
|
+
|
|
10
|
+
import type {Coord} from "../../interactive2/types";
|
|
11
|
+
import type {
|
|
12
|
+
PerseusGraphType,
|
|
13
|
+
PerseusGraphTypeAngle,
|
|
14
|
+
PerseusGraphTypeCircle,
|
|
15
|
+
PerseusGraphTypeLinear,
|
|
16
|
+
PerseusGraphTypeLinearSystem,
|
|
17
|
+
PerseusGraphTypePoint,
|
|
18
|
+
PerseusGraphTypePolygon,
|
|
19
|
+
PerseusGraphTypeQuadratic,
|
|
20
|
+
PerseusGraphTypeRay,
|
|
21
|
+
PerseusGraphTypeSegment,
|
|
22
|
+
PerseusGraphTypeSinusoid,
|
|
23
|
+
PerseusImageBackground,
|
|
24
|
+
PerseusRenderer,
|
|
25
|
+
} from "../../perseus-types";
|
|
26
|
+
|
|
27
|
+
const randomCoord = (): Coord => {
|
|
28
|
+
return [randomInteger(1, 100), randomInteger(1, 100)];
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const randomGraphTypeAngle = (): PerseusGraphTypeAngle => {
|
|
32
|
+
return {
|
|
33
|
+
type: "angle",
|
|
34
|
+
showAngles: randomBoolean(),
|
|
35
|
+
allowReflexAngles: randomBoolean(),
|
|
36
|
+
angleOffsetDeg: randomInteger(1, 90),
|
|
37
|
+
snapDegrees: randomInteger(1, 90),
|
|
38
|
+
match: "congruent",
|
|
39
|
+
// must be 3 coords for an angle graph
|
|
40
|
+
coords: arrayOfLength(3).map(randomCoord),
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const randomGraphTypeCircle = (): PerseusGraphTypeCircle => {
|
|
45
|
+
return {
|
|
46
|
+
type: "circle",
|
|
47
|
+
center: randomCoord(),
|
|
48
|
+
radius: randomInteger(1, 20),
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const randomGraphTypeLinear = (): PerseusGraphTypeLinear => {
|
|
53
|
+
return {
|
|
54
|
+
type: "linear",
|
|
55
|
+
coords: arrayOfLength(2).map(randomCoord),
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const randomGraphTypeLinearSystem = (): PerseusGraphTypeLinearSystem => {
|
|
60
|
+
return {
|
|
61
|
+
type: "linear-system",
|
|
62
|
+
coords: arrayOfLength(2).map(() => arrayOfLength(2).map(randomCoord)),
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const randomGraphTypePoint = (): PerseusGraphTypePoint => {
|
|
67
|
+
let minCoords = 1;
|
|
68
|
+
const numPoints = randomElement(["unlimited", randomInteger(1, 10)]);
|
|
69
|
+
if (typeof numPoints === "number") {
|
|
70
|
+
minCoords = numPoints;
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
type: "point",
|
|
74
|
+
numPoints: typeof numPoints === "number" ? numPoints : "unlimited",
|
|
75
|
+
coords: arrayOfLength(randomInteger(minCoords, 10)).map(randomCoord),
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const randomGraphTypePolygon = (): PerseusGraphTypePolygon => {
|
|
80
|
+
// Note Jhead - I saw this create at least one broken graph but didn't figure out why
|
|
81
|
+
return {
|
|
82
|
+
type: "polygon",
|
|
83
|
+
numSides: randomElement([
|
|
84
|
+
"unlimited",
|
|
85
|
+
undefined,
|
|
86
|
+
randomInteger(3, 30),
|
|
87
|
+
randomInteger(3, 30),
|
|
88
|
+
]),
|
|
89
|
+
showAngles: randomBoolean(),
|
|
90
|
+
showSides: randomBoolean(),
|
|
91
|
+
snapTo: randomElement(["grid", "angles", "sides"]),
|
|
92
|
+
match: randomElement(["similar", "congruent", "approx", undefined]),
|
|
93
|
+
coords: arrayOfLength(randomInteger(3, 20)).map(randomCoord),
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const randomGraphTypeQuadratic = (): PerseusGraphTypeQuadratic => {
|
|
98
|
+
return {
|
|
99
|
+
type: "quadratic",
|
|
100
|
+
coords: arrayOfLength(3).map(randomCoord),
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const randomGraphTypeRay = (): PerseusGraphTypeRay => {
|
|
105
|
+
return {
|
|
106
|
+
type: "ray",
|
|
107
|
+
coords: arrayOfLength(2).map(randomCoord),
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const randomGraphTypeSegment = (): PerseusGraphTypeSegment => {
|
|
112
|
+
const numSegments = randomInteger(1, 6);
|
|
113
|
+
return {
|
|
114
|
+
type: "segment",
|
|
115
|
+
numSegments,
|
|
116
|
+
coords: arrayOfLength(numSegments).map(() => [
|
|
117
|
+
randomCoord(),
|
|
118
|
+
randomCoord(),
|
|
119
|
+
]),
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const randomGraphTypeSinusoid = (): PerseusGraphTypeSinusoid => {
|
|
124
|
+
return {
|
|
125
|
+
type: "sinusoid",
|
|
126
|
+
coords: arrayOfLength(2).map(randomCoord),
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const randomBackgroundImage = (): PerseusImageBackground => {
|
|
131
|
+
const height = randomInteger(200, 500);
|
|
132
|
+
const width = randomInteger(200, 500);
|
|
133
|
+
const graphieUrl =
|
|
134
|
+
"web+graphie://ka-perseus-graphie.s3.amazonaws.com/a1b7a05c177742523250b64a3995c9b37aac3399";
|
|
135
|
+
const placeKittenUrl = `https://placekitten.com/g/${width}/${height}`;
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
scale: randomInteger(8, 12) / 10,
|
|
139
|
+
bottom: randomInteger(0, 10) - 5,
|
|
140
|
+
url: randomElement([graphieUrl, placeKittenUrl, null]),
|
|
141
|
+
height,
|
|
142
|
+
width,
|
|
143
|
+
left: randomInteger(0, 10) - 5,
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const randomInteractiveGraphGenerator = (
|
|
148
|
+
type?: string,
|
|
149
|
+
): PerseusRenderer => {
|
|
150
|
+
const randomRange = (): [number, number] => {
|
|
151
|
+
const val1 = randomInteger(0, 400) - 200;
|
|
152
|
+
const val2 = randomInteger(0, 400) - 200;
|
|
153
|
+
if (val1 < val2) {
|
|
154
|
+
return [val1, val2];
|
|
155
|
+
}
|
|
156
|
+
return [val2, val1];
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const graphType =
|
|
160
|
+
type ||
|
|
161
|
+
randomElement([
|
|
162
|
+
"angle",
|
|
163
|
+
"circle",
|
|
164
|
+
"linear",
|
|
165
|
+
"linear-system",
|
|
166
|
+
"point",
|
|
167
|
+
"polygon",
|
|
168
|
+
"quadratic",
|
|
169
|
+
"ray",
|
|
170
|
+
"segment",
|
|
171
|
+
"sinusoid",
|
|
172
|
+
]);
|
|
173
|
+
|
|
174
|
+
let graph: PerseusGraphType;
|
|
175
|
+
let correct: PerseusGraphType;
|
|
176
|
+
|
|
177
|
+
switch (graphType) {
|
|
178
|
+
case "angle": {
|
|
179
|
+
graph = randomGraphTypeAngle();
|
|
180
|
+
correct = randomGraphTypeAngle();
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
case "circle": {
|
|
184
|
+
graph = randomGraphTypeCircle();
|
|
185
|
+
correct = randomGraphTypeCircle();
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
case "linear": {
|
|
189
|
+
graph = randomGraphTypeLinear();
|
|
190
|
+
correct = randomGraphTypeLinear();
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
case "linear-system": {
|
|
194
|
+
graph = randomGraphTypeLinearSystem();
|
|
195
|
+
correct = randomGraphTypeLinearSystem();
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
case "point": {
|
|
199
|
+
graph = randomGraphTypePoint();
|
|
200
|
+
correct = randomGraphTypePoint();
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
case "polygon": {
|
|
204
|
+
graph = randomGraphTypePolygon();
|
|
205
|
+
correct = randomGraphTypePolygon();
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
case "quadratic": {
|
|
209
|
+
graph = randomGraphTypeQuadratic();
|
|
210
|
+
correct = randomGraphTypeQuadratic();
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
case "ray": {
|
|
214
|
+
graph = randomGraphTypeRay();
|
|
215
|
+
correct = randomGraphTypeRay();
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case "segment": {
|
|
219
|
+
graph = randomGraphTypeSegment();
|
|
220
|
+
correct = randomGraphTypeSegment();
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
case "sinusoid": {
|
|
224
|
+
graph = randomGraphTypeSinusoid();
|
|
225
|
+
correct = randomGraphTypeSinusoid();
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
default: {
|
|
229
|
+
graph = randomGraphTypeAngle();
|
|
230
|
+
correct = randomGraphTypeAngle();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
content: `[[\u2603 interactive-graph 1]]`,
|
|
236
|
+
images: {},
|
|
237
|
+
widgets: {
|
|
238
|
+
"interactive-graph 1": {
|
|
239
|
+
graded: randomBoolean(),
|
|
240
|
+
version: {
|
|
241
|
+
major: 0,
|
|
242
|
+
minor: 0,
|
|
243
|
+
},
|
|
244
|
+
static: false, //always false for this widget
|
|
245
|
+
type: "interactive-graph",
|
|
246
|
+
options: {
|
|
247
|
+
rulerTicks: randomInteger(1, 100),
|
|
248
|
+
showProtractor: randomBoolean(0.2),
|
|
249
|
+
graph,
|
|
250
|
+
snapStep: [
|
|
251
|
+
randomInteger(1, 10) / 10,
|
|
252
|
+
randomInteger(1, 10) / 10,
|
|
253
|
+
],
|
|
254
|
+
labels: [randomLetter() || "x", randomLetter() || "y"],
|
|
255
|
+
step: [randomInteger(1, 10), randomInteger(1, 10)],
|
|
256
|
+
gridStep: [randomInteger(1, 10), randomInteger(1, 10)],
|
|
257
|
+
backgroundImage: randomBackgroundImage(),
|
|
258
|
+
range: [randomRange(), randomRange()],
|
|
259
|
+
showRuler: randomBoolean(0.2),
|
|
260
|
+
markings:
|
|
261
|
+
randomElement(["none", "graph", "grid"]) || "none",
|
|
262
|
+
rulerLabel: randomElement(["", randomWord()]) || "",
|
|
263
|
+
correct,
|
|
264
|
+
},
|
|
265
|
+
alignment: "default",
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
};
|