@khanacademy/perseus 19.1.5 → 19.1.6
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 +1 -1
- package/dist/index.js +1 -1
- package/package.json +11 -8
- package/.babelrc.js +0 -8
- package/.eslintrc.js +0 -12
- package/CHANGELOG.md +0 -1432
- package/src/__stories__/article-renderer.stories.tsx +0 -100
- package/src/__stories__/hints-renderer.stories.tsx +0 -55
- package/src/__stories__/server-item-renderer.stories.tsx +0 -44
- package/src/__testdata__/article-renderer.testdata.ts +0 -212
- package/src/__testdata__/graphie.testdata.ts +0 -51
- package/src/__testdata__/renderer.testdata.ts +0 -274
- package/src/__testdata__/server-item-renderer.testdata.ts +0 -171
- package/src/__tests__/__snapshots__/perseus-markdown.test.ts.snap +0 -507
- package/src/__tests__/__snapshots__/renderer.test.tsx.snap +0 -525
- package/src/__tests__/__snapshots__/server-item-renderer.test.tsx.snap +0 -285
- package/src/__tests__/a11y.test.ts +0 -303
- package/src/__tests__/article-renderer.test.tsx +0 -130
- package/src/__tests__/error-boundary.test.tsx +0 -76
- package/src/__tests__/extract-perseus-data.test.ts +0 -1027
- package/src/__tests__/init.test.ts +0 -10
- package/src/__tests__/jipt-paragraphs.test.ts +0 -32
- package/src/__tests__/mock-asset-loading-widget.tsx +0 -53
- package/src/__tests__/mock-widget.tsx +0 -33
- package/src/__tests__/perseus-markdown.test.ts +0 -764
- package/src/__tests__/renderability.test.ts +0 -480
- package/src/__tests__/renderer-api.test.tsx +0 -302
- package/src/__tests__/renderer.test.tsx +0 -1816
- package/src/__tests__/server-item-renderer.test.tsx +0 -582
- package/src/__tests__/test-items/image-item.ts +0 -24
- package/src/__tests__/test-items/input-number-1-item.ts +0 -26
- package/src/__tests__/test-items/input-number-2-item.ts +0 -38
- package/src/__tests__/test-items/table-item.ts +0 -29
- package/src/__tests__/tex-wrangler.test.ts +0 -82
- package/src/__tests__/util.test.ts +0 -40
- package/src/__tests__/widget-container.test.tsx +0 -126
- package/src/__tests__/widgets.test.ts +0 -91
- package/src/a11y.ts +0 -45
- package/src/all-widgets.ts +0 -9
- package/src/article-renderer.tsx +0 -237
- package/src/asset-context.ts +0 -30
- package/src/basic-widgets.ts +0 -16
- package/src/components/__stories__/button-group.stories.tsx +0 -55
- package/src/components/__stories__/fixed-to-responsive.stories.tsx +0 -141
- package/src/components/__stories__/graph.stories.tsx +0 -28
- package/src/components/__stories__/graphie.stories.tsx +0 -33
- package/src/components/__stories__/hud.stories.tsx +0 -35
- package/src/components/__stories__/icon.stories.tsx +0 -63
- package/src/components/__stories__/image-loader.stories.tsx +0 -63
- package/src/components/__stories__/info-tip.stories.tsx +0 -31
- package/src/components/__stories__/inline-icon.stories.tsx +0 -40
- package/src/components/__stories__/input-with-examples.stories.tsx +0 -53
- package/src/components/__stories__/lint.stories.tsx +0 -86
- package/src/components/__stories__/math-input.stories.tsx +0 -43
- package/src/components/__stories__/math-output.stories.tsx +0 -23
- package/src/components/__stories__/multi-button-group.stories.tsx +0 -66
- package/src/components/__stories__/number-input.stories.tsx +0 -41
- package/src/components/__stories__/prop-check-box.stories.tsx +0 -65
- package/src/components/__stories__/range-input.stories.tsx +0 -29
- package/src/components/__stories__/simple-keypad-input.stories.tsx +0 -27
- package/src/components/__stories__/sortable.stories.tsx +0 -77
- package/src/components/__stories__/stub-tag-editor.stories.tsx +0 -45
- package/src/components/__stories__/svg-image.stories.tsx +0 -74
- package/src/components/__stories__/tex.stories.tsx +0 -23
- package/src/components/__stories__/text-input.stories.tsx +0 -33
- package/src/components/__stories__/text-list-editor.stories.tsx +0 -32
- package/src/components/__stories__/tooltip.stories.tsx +0 -52
- package/src/components/__stories__/zoomable-tex.stories.tsx +0 -41
- package/src/components/__stories__/zoomable.stories.tsx +0 -49
- package/src/components/__testdata__/sorter.testdata.ts +0 -19
- package/src/components/__tests__/__snapshots__/sortable.test.tsx.snap +0 -163
- package/src/components/__tests__/__snapshots__/sorter.test.tsx.snap +0 -265
- package/src/components/__tests__/math-input.test.tsx +0 -147
- package/src/components/__tests__/number-input.test.tsx +0 -128
- package/src/components/__tests__/sortable.test.tsx +0 -269
- package/src/components/__tests__/sorter.test.tsx +0 -114
- package/src/components/__tests__/zoomable.test.tsx +0 -399
- package/src/components/button-group.tsx +0 -130
- package/src/components/constants.ts +0 -14
- package/src/components/fixed-to-responsive.tsx +0 -169
- package/src/components/graph.tsx +0 -444
- package/src/components/graphie-classes.ts +0 -153
- package/src/components/graphie-movables.ts +0 -338
- package/src/components/graphie.tsx +0 -400
- package/src/components/highlighting/highlightable-content.tsx +0 -240
- package/src/components/highlighting/highlights.ts +0 -207
- package/src/components/highlighting/ranges.ts +0 -304
- package/src/components/highlighting/types.ts +0 -73
- package/src/components/highlighting/ui/highlight-renderer.tsx +0 -172
- package/src/components/highlighting/ui/highlight-set-renderer.tsx +0 -185
- package/src/components/highlighting/ui/highlight-tooltip.tsx +0 -141
- package/src/components/highlighting/ui/highlighting-ui.tsx +0 -143
- package/src/components/highlighting/ui/selection-tracker.tsx +0 -186
- package/src/components/highlighting/ui/types.ts +0 -21
- package/src/components/highlighting/ui/util.ts +0 -165
- package/src/components/highlighting/word-indexer.tsx +0 -152
- package/src/components/hud.tsx +0 -167
- package/src/components/icon.tsx +0 -141
- package/src/components/image-loader.tsx +0 -177
- package/src/components/info-tip/info-tip.tsx +0 -144
- package/src/components/info-tip.tsx +0 -39
- package/src/components/inline-icon.tsx +0 -60
- package/src/components/input-with-examples.tsx +0 -249
- package/src/components/lint.tsx +0 -459
- package/src/components/math-input.tsx +0 -474
- package/src/components/math-output.tsx +0 -134
- package/src/components/multi-button-group.tsx +0 -135
- package/src/components/number-input.tsx +0 -249
- package/src/components/prop-check-box.tsx +0 -79
- package/src/components/range-input.tsx +0 -60
- package/src/components/simple-keypad-input.tsx +0 -88
- package/src/components/sortable.tsx +0 -958
- package/src/components/stub-tag-editor.tsx +0 -53
- package/src/components/svg-image.tsx +0 -779
- package/src/components/tex.tsx +0 -67
- package/src/components/text-input.tsx +0 -129
- package/src/components/text-list-editor.tsx +0 -166
- package/src/components/tooltip.tsx +0 -410
- package/src/components/visibility-observer/__stories__/visibility-observer.stories.tsx +0 -141
- package/src/components/visibility-observer/create-visibility-observer.ts +0 -108
- package/src/components/visibility-observer/get-scroll-parent.ts +0 -46
- package/src/components/visibility-observer/visibility-observer.tsx +0 -124
- package/src/components/zoomable-tex.tsx +0 -78
- package/src/components/zoomable.tsx +0 -304
- package/src/components.ts +0 -15
- package/src/definition-context.tsx +0 -59
- package/src/dependencies.ts +0 -40
- package/src/error-boundary.tsx +0 -70
- package/src/extra-widgets.ts +0 -75
- package/src/hint-renderer.tsx +0 -151
- package/src/hints-renderer.tsx +0 -372
- package/src/icon-paths.ts +0 -119
- package/src/index.ts +0 -171
- package/src/init.ts +0 -56
- package/src/interaction-tracker.ts +0 -62
- package/src/interactive2/__tests__/movable-mock.ts +0 -55
- package/src/interactive2/__tests__/movable-point.test.ts +0 -144
- package/src/interactive2/interactive-util.test.ts +0 -93
- package/src/interactive2/interactive-util.ts +0 -159
- package/src/interactive2/movable-helper-methods.ts +0 -166
- package/src/interactive2/movable-line-options.ts +0 -419
- package/src/interactive2/movable-line.ts +0 -307
- package/src/interactive2/movable-point-options.ts +0 -224
- package/src/interactive2/movable-point.tsx +0 -585
- package/src/interactive2/movable-polygon-options.ts +0 -389
- package/src/interactive2/movable-polygon.ts +0 -360
- package/src/interactive2/movable.test.ts +0 -249
- package/src/interactive2/movable.ts +0 -437
- package/src/interactive2/objective_.ts +0 -79
- package/src/interactive2/types.ts +0 -17
- package/src/interactive2/wrapped-defaults.ts +0 -87
- package/src/interactive2/wrapped-ellipse.ts +0 -94
- package/src/interactive2/wrapped-line.ts +0 -116
- package/src/interactive2/wrapped-path.ts +0 -51
- package/src/interactive2.ts +0 -70
- package/src/item-version.ts +0 -10
- package/src/jipt-paragraphs.ts +0 -49
- package/src/jquery.mobile.vmouse.js +0 -545
- package/src/loading-context.ts +0 -21
- package/src/logging/log.test.ts +0 -39
- package/src/logging/log.ts +0 -97
- package/src/mixins/__tests__/widget-prop-denylist.test.ts +0 -21
- package/src/mixins/changeable.ts +0 -133
- package/src/mixins/editor-jsonify.ts +0 -13
- package/src/mixins/widget-jsonify-deprecated.ts +0 -23
- package/src/mixins/widget-prop-denylist.ts +0 -48
- package/src/multi-items/__stories__/multi-renderer.stories.tsx +0 -83
- package/src/multi-items/__testdata__/multi-renderer.testdata.ts +0 -172
- package/src/multi-items/__tests__/__snapshots__/multi-renderer.test.tsx.snap +0 -1164
- package/src/multi-items/__tests__/items.test.ts +0 -240
- package/src/multi-items/__tests__/multi-renderer.test.tsx +0 -914
- package/src/multi-items/__tests__/prop-type-builders.test.ts +0 -133
- package/src/multi-items/__tests__/shapes.test.ts +0 -43
- package/src/multi-items/__tests__/trees.test.ts +0 -317
- package/src/multi-items/item-types.ts +0 -56
- package/src/multi-items/items.ts +0 -176
- package/src/multi-items/multi-renderer.tsx +0 -569
- package/src/multi-items/prop-type-builders.ts +0 -106
- package/src/multi-items/shape-types.ts +0 -52
- package/src/multi-items/shapes.ts +0 -52
- package/src/multi-items/tree-types.ts +0 -47
- package/src/multi-items/trees.ts +0 -360
- package/src/multi-items.ts +0 -75
- package/src/perseus-api.tsx +0 -188
- package/src/perseus-env.ts +0 -27
- package/src/perseus-error.ts +0 -17
- package/src/perseus-markdown.tsx +0 -405
- package/src/perseus-types.ts +0 -1366
- package/src/perseus-version.ts +0 -9
- package/src/question-paragraph.tsx +0 -34
- package/src/renderability.ts +0 -109
- package/src/renderer.tsx +0 -2000
- package/src/server-item-renderer.tsx +0 -495
- package/src/sigfigs.ts +0 -490
- package/src/styles/articles.less +0 -749
- package/src/styles/constants.ts +0 -94
- package/src/styles/global-constants.ts +0 -343
- package/src/styles/global-styles.ts +0 -306
- package/src/styles/khan-exercise.css +0 -1468
- package/src/styles/media-queries.ts +0 -47
- package/src/styles/perseus-renderer.less +0 -732
- package/src/styles/shared.ts +0 -130
- package/src/styles/util.less +0 -47
- package/src/styles/variables.boostrap.less +0 -277
- package/src/styles/variables.less +0 -56
- package/src/styles/variables.shared.less +0 -340
- package/src/styles/widgets/categorizer.less +0 -38
- package/src/styles/widgets/dropdown.less +0 -3
- package/src/styles/widgets/expression.less +0 -65
- package/src/styles/widgets/graded-group.less +0 -36
- package/src/styles/widgets/grapher.less +0 -12
- package/src/styles/widgets/group.less +0 -19
- package/src/styles/widgets/image.less +0 -54
- package/src/styles/widgets/interactive-graph.less +0 -64
- package/src/styles/widgets/label-image.less +0 -57
- package/src/styles/widgets/matcher.less +0 -8
- package/src/styles/widgets/matrix.less +0 -126
- package/src/styles/widgets/measurer.less +0 -20
- package/src/styles/widgets/orderer.less +0 -98
- package/src/styles/widgets/passage.less +0 -154
- package/src/styles/widgets/plotter.less +0 -31
- package/src/styles/widgets/radio.less +0 -58
- package/src/styles/widgets/reaction-diagram.less +0 -35
- package/src/styles/widgets/sequence.less +0 -5
- package/src/styles/widgets/sortable.less +0 -113
- package/src/styles/widgets/table.less +0 -57
- package/src/styles/widgets/unit.less +0 -15
- package/src/tex-wrangler.ts +0 -182
- package/src/translation-linter.ts +0 -126
- package/src/traversal.ts +0 -138
- package/src/types.ts +0 -513
- package/src/util/a11y.ts +0 -23
- package/src/util/answer-types.test.ts +0 -114
- package/src/util/answer-types.ts +0 -876
- package/src/util/colors.ts +0 -93
- package/src/util/drawing-transform.test.ts +0 -67
- package/src/util/drawing-transform.ts +0 -102
- package/src/util/extract-perseus-data.ts +0 -539
- package/src/util/fix-passage-refs.ts +0 -121
- package/src/util/geometry.test.ts +0 -75
- package/src/util/geometry.ts +0 -312
- package/src/util/graph-bounds.ts +0 -23
- package/src/util/graph-utils.ts +0 -15
- package/src/util/graphie.test.ts +0 -1277
- package/src/util/graphie.ts +0 -1576
- package/src/util/interactive.ts +0 -4174
- package/src/util/interval.ts +0 -4
- package/src/util/katex-preprocess.ts +0 -14
- package/src/util/math.test.ts +0 -11
- package/src/util/math.ts +0 -294
- package/src/util/mobile-native-utils.test.ts +0 -22
- package/src/util/mobile-native-utils.ts +0 -12
- package/src/util/parse-perseus-json.test.ts +0 -10
- package/src/util/parse-perseus-json.ts +0 -13
- package/src/util/react-render.tsx +0 -27
- package/src/util/register-all-widgets-for-testing.ts +0 -13
- package/src/util/scroll-utils.ts +0 -155
- package/src/util/sizing-utils.ts +0 -46
- package/src/util/tex.ts +0 -73
- package/src/util/url-utils.ts +0 -34
- package/src/util.graphie.test.ts +0 -45
- package/src/util.graphie.ts +0 -59
- package/src/util.ts +0 -966
- package/src/version.ts +0 -10
- package/src/widget-container.tsx +0 -199
- package/src/widgets/__stories__/categorizer.stories.tsx +0 -14
- package/src/widgets/__stories__/cs-program.stories.tsx +0 -14
- package/src/widgets/__stories__/definition.stories.tsx +0 -114
- package/src/widgets/__stories__/dropdown.stories.tsx +0 -14
- package/src/widgets/__stories__/explanation.stories.tsx +0 -18
- package/src/widgets/__stories__/expression.stories.tsx +0 -145
- package/src/widgets/__stories__/graded-group-set.stories.tsx +0 -31
- package/src/widgets/__stories__/graded-group.stories.tsx +0 -31
- package/src/widgets/__stories__/grapher.stories.tsx +0 -46
- package/src/widgets/__stories__/group.stories.tsx +0 -14
- package/src/widgets/__stories__/iframe.stories.tsx +0 -14
- package/src/widgets/__stories__/image.stories.tsx +0 -85
- package/src/widgets/__stories__/input-number.stories.tsx +0 -101
- package/src/widgets/__stories__/interaction.stories.tsx +0 -14
- package/src/widgets/__stories__/interactive-graph.stories.tsx +0 -60
- package/src/widgets/__stories__/label-image.stories.tsx +0 -130
- package/src/widgets/__stories__/matcher.stories.tsx +0 -14
- package/src/widgets/__stories__/matrix.stories.tsx +0 -14
- package/src/widgets/__stories__/number-line.stories.tsx +0 -14
- package/src/widgets/__stories__/numeric-input.stories.tsx +0 -105
- package/src/widgets/__stories__/orderer.stories.tsx +0 -18
- package/src/widgets/__stories__/passage-ref.stories.tsx +0 -33
- package/src/widgets/__stories__/passage.stories.tsx +0 -39
- package/src/widgets/__stories__/plotter.stories.tsx +0 -27
- package/src/widgets/__stories__/radio.stories.tsx +0 -86
- package/src/widgets/__stories__/random-widgets.stories.tsx +0 -59
- package/src/widgets/__stories__/sorter.stories.tsx +0 -14
- package/src/widgets/__stories__/test-keypad-context-wrapper.tsx +0 -66
- package/src/widgets/__stories__/unit.stories.tsx +0 -28
- package/src/widgets/__stories__/video-transcript-link.stories.tsx +0 -23
- package/src/widgets/__stories__/video.stories.tsx +0 -22
- package/src/widgets/__testdata__/base-radio.testdata.ts +0 -21
- package/src/widgets/__testdata__/categorizer.testdata.ts +0 -89
- package/src/widgets/__testdata__/cs-program.testdata.ts +0 -27
- package/src/widgets/__testdata__/dropdown.testdata.ts +0 -71
- package/src/widgets/__testdata__/explanation.testdata.ts +0 -85
- package/src/widgets/__testdata__/expression.testdata.ts +0 -198
- package/src/widgets/__testdata__/graded-group-set.testdata.ts +0 -356
- package/src/widgets/__testdata__/graded-group.testdata.ts +0 -77
- package/src/widgets/__testdata__/grapher.testdata.ts +0 -374
- package/src/widgets/__testdata__/group.testdata.ts +0 -153
- package/src/widgets/__testdata__/iframe.testdata.ts +0 -35
- package/src/widgets/__testdata__/image.testdata.ts +0 -36
- package/src/widgets/__testdata__/input-number.testdata.ts +0 -72
- package/src/widgets/__testdata__/interaction.testdata.ts +0 -262
- package/src/widgets/__testdata__/interactive-graph-random.testdata.ts +0 -269
- package/src/widgets/__testdata__/interactive-graph.testdata.ts +0 -644
- package/src/widgets/__testdata__/label-image.testdata.ts +0 -308
- package/src/widgets/__testdata__/matcher.testdata.ts +0 -33
- package/src/widgets/__testdata__/matrix.testdata.ts +0 -28
- package/src/widgets/__testdata__/number-line.testdata.ts +0 -30
- package/src/widgets/__testdata__/numeric-input.testdata.ts +0 -265
- package/src/widgets/__testdata__/orderer.testdata.ts +0 -123
- package/src/widgets/__testdata__/passage-ref.testdata.ts +0 -101
- package/src/widgets/__testdata__/passage.testdata.ts +0 -95
- package/src/widgets/__testdata__/plotter.testdata.ts +0 -36
- package/src/widgets/__testdata__/radio.testdata.ts +0 -324
- package/src/widgets/__testdata__/randomizers.ts +0 -51
- package/src/widgets/__testdata__/sorter.testdata.ts +0 -19
- package/src/widgets/__testdata__/video.testdata.ts +0 -61
- package/src/widgets/__tests__/__snapshots__/categorizer.test.ts.snap +0 -940
- package/src/widgets/__tests__/__snapshots__/cs-program.test.ts.snap +0 -67
- package/src/widgets/__tests__/__snapshots__/definition.test.ts.snap +0 -97
- package/src/widgets/__tests__/__snapshots__/dropdown.test.ts.snap +0 -133
- package/src/widgets/__tests__/__snapshots__/explanation.test.ts.snap +0 -425
- package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt.test.ts.snap +0 -729
- package/src/widgets/__tests__/__snapshots__/graded-group-set.test.ts.snap +0 -339
- package/src/widgets/__tests__/__snapshots__/graded-group.test.ts.snap +0 -801
- package/src/widgets/__tests__/__snapshots__/grapher.test.ts.snap +0 -2673
- package/src/widgets/__tests__/__snapshots__/group.test.tsx.snap +0 -1211
- package/src/widgets/__tests__/__snapshots__/iframe.test.ts.snap +0 -61
- package/src/widgets/__tests__/__snapshots__/image.test.ts.snap +0 -378
- package/src/widgets/__tests__/__snapshots__/input-number.test.ts.snap +0 -495
- package/src/widgets/__tests__/__snapshots__/interaction.test.ts.snap +0 -292
- package/src/widgets/__tests__/__snapshots__/interactive-graph.test.ts.snap +0 -2889
- package/src/widgets/__tests__/__snapshots__/matcher.test.tsx.snap +0 -883
- package/src/widgets/__tests__/__snapshots__/matrix.test.ts.snap +0 -514
- package/src/widgets/__tests__/__snapshots__/number-line.test.ts.snap +0 -814
- package/src/widgets/__tests__/__snapshots__/numeric-input.test.ts.snap +0 -470
- package/src/widgets/__tests__/__snapshots__/orderer.test.ts.snap +0 -243
- package/src/widgets/__tests__/__snapshots__/passage-ref.test.ts.snap +0 -434
- package/src/widgets/__tests__/__snapshots__/passage.test.tsx.snap +0 -1956
- package/src/widgets/__tests__/__snapshots__/plotter.test.tsx.snap +0 -827
- package/src/widgets/__tests__/__snapshots__/radio.test.ts.snap +0 -4237
- package/src/widgets/__tests__/__snapshots__/unit.test.tsx.snap +0 -20
- package/src/widgets/__tests__/__snapshots__/video.test.ts.snap +0 -153
- package/src/widgets/__tests__/categorizer.test.ts +0 -203
- package/src/widgets/__tests__/cs-program.test.ts +0 -43
- package/src/widgets/__tests__/definition.test.ts +0 -136
- package/src/widgets/__tests__/dropdown.test.ts +0 -105
- package/src/widgets/__tests__/explanation.test.ts +0 -198
- package/src/widgets/__tests__/expression-mobile.test.tsx +0 -363
- package/src/widgets/__tests__/expression.test.tsx +0 -484
- package/src/widgets/__tests__/graded-group-set-jipt.test.ts +0 -30
- package/src/widgets/__tests__/graded-group-set.test.ts +0 -288
- package/src/widgets/__tests__/graded-group.test.ts +0 -232
- package/src/widgets/__tests__/grapher.cypress.ts +0 -576
- package/src/widgets/__tests__/grapher.test.ts +0 -33
- package/src/widgets/__tests__/group.test.tsx +0 -510
- package/src/widgets/__tests__/iframe.test.ts +0 -45
- package/src/widgets/__tests__/image.test.ts +0 -62
- package/src/widgets/__tests__/input-number.test.ts +0 -354
- package/src/widgets/__tests__/interaction.test.ts +0 -31
- package/src/widgets/__tests__/interactive-graph.cypress.ts +0 -815
- package/src/widgets/__tests__/interactive-graph.test.ts +0 -106
- package/src/widgets/__tests__/matcher.test.tsx +0 -176
- package/src/widgets/__tests__/matrix.test.ts +0 -82
- package/src/widgets/__tests__/number-line.test.ts +0 -79
- package/src/widgets/__tests__/numeric-input.test.ts +0 -575
- package/src/widgets/__tests__/orderer.test.ts +0 -73
- package/src/widgets/__tests__/passage-ref.test.ts +0 -168
- package/src/widgets/__tests__/passage.test.tsx +0 -224
- package/src/widgets/__tests__/plotter.test.tsx +0 -69
- package/src/widgets/__tests__/radio/base-radio.test.tsx +0 -470
- package/src/widgets/__tests__/radio/choice-icon.test.tsx +0 -90
- package/src/widgets/__tests__/radio/choice.test.tsx +0 -310
- package/src/widgets/__tests__/radio/focus-ring.test.tsx +0 -22
- package/src/widgets/__tests__/radio/option-status.test.tsx +0 -77
- package/src/widgets/__tests__/radio.test.ts +0 -807
- package/src/widgets/__tests__/randomizers.test.ts +0 -53
- package/src/widgets/__tests__/renderQuestion.tsx +0 -101
- package/src/widgets/__tests__/unit.test.tsx +0 -168
- package/src/widgets/__tests__/video.test.ts +0 -69
- package/src/widgets/categorizer.tsx +0 -344
- package/src/widgets/cs-program.tsx +0 -227
- package/src/widgets/definition.tsx +0 -121
- package/src/widgets/deprecated-standin/__stories__/deprecated-standin.stories.tsx +0 -30
- package/src/widgets/deprecated-standin/__tests__/__snapshots__/deprecated-standin.test.ts.snap +0 -70
- package/src/widgets/deprecated-standin/__tests__/deprecated-standin.test.ts +0 -93
- package/src/widgets/deprecated-standin/index.tsx +0 -58
- package/src/widgets/dropdown.tsx +0 -142
- package/src/widgets/explanation.tsx +0 -317
- package/src/widgets/expression.tsx +0 -707
- package/src/widgets/graded-group-answer-bar.tsx +0 -142
- package/src/widgets/graded-group-set.tsx +0 -338
- package/src/widgets/graded-group.tsx +0 -471
- package/src/widgets/grapher/util.tsx +0 -799
- package/src/widgets/grapher.tsx +0 -660
- package/src/widgets/group.tsx +0 -195
- package/src/widgets/iframe.tsx +0 -195
- package/src/widgets/image.tsx +0 -312
- package/src/widgets/input-number.tsx +0 -412
- package/src/widgets/interaction/arrow-picker.tsx +0 -38
- package/src/widgets/interaction/color-picker.tsx +0 -69
- package/src/widgets/interaction/dash-picker.tsx +0 -45
- package/src/widgets/interaction/element-container.tsx +0 -100
- package/src/widgets/interaction.tsx +0 -826
- package/src/widgets/interactive-graph.tsx +0 -2597
- package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +0 -91
- package/src/widgets/label-image/__stories__/answer-pill.stories.tsx +0 -62
- package/src/widgets/label-image/__stories__/marker.stories.tsx +0 -171
- package/src/widgets/label-image/__tests__/answer-pill.test.tsx +0 -75
- package/src/widgets/label-image/__tests__/hide-answers-toggle.test.tsx +0 -39
- package/src/widgets/label-image/answer-choices.tsx +0 -88
- package/src/widgets/label-image/answer-pill.tsx +0 -103
- package/src/widgets/label-image/hide-answers-toggle.tsx +0 -40
- package/src/widgets/label-image/marker.tsx +0 -268
- package/src/widgets/label-image/strings.ts +0 -5
- package/src/widgets/label-image/types.ts +0 -27
- package/src/widgets/label-image.test.ts +0 -928
- package/src/widgets/label-image.tsx +0 -819
- package/src/widgets/lights-puzzle.tsx +0 -359
- package/src/widgets/matcher.tsx +0 -298
- package/src/widgets/matrix.tsx +0 -634
- package/src/widgets/measurer.tsx +0 -222
- package/src/widgets/molecule/__tests__/layout.test.ts +0 -162
- package/src/widgets/molecule/__tests__/parser.test.ts +0 -155
- package/src/widgets/molecule/molecule-drawing.ts +0 -173
- package/src/widgets/molecule/molecule-layout.ts +0 -362
- package/src/widgets/molecule/smiles-parser.ts +0 -296
- package/src/widgets/molecule.tsx +0 -172
- package/src/widgets/number-line.tsx +0 -862
- package/src/widgets/numeric-input.tsx +0 -576
- package/src/widgets/orderer.tsx +0 -786
- package/src/widgets/passage/__tests__/passage-markdown.test.ts +0 -376
- package/src/widgets/passage/passage-markdown.tsx +0 -475
- package/src/widgets/passage-ref-target.tsx +0 -76
- package/src/widgets/passage-ref.tsx +0 -213
- package/src/widgets/passage.tsx +0 -582
- package/src/widgets/plotter.tsx +0 -1194
- package/src/widgets/radio/__stories__/base-radio.stories.tsx +0 -196
- package/src/widgets/radio/__stories__/choice-icon.stories.tsx +0 -166
- package/src/widgets/radio/__stories__/choice-none-above.stories.tsx +0 -40
- package/src/widgets/radio/__stories__/choice.stories.tsx +0 -137
- package/src/widgets/radio/__stories__/focus-ring.stories.tsx +0 -43
- package/src/widgets/radio/__stories__/option-status.stories.tsx +0 -103
- package/src/widgets/radio/base-radio.tsx +0 -511
- package/src/widgets/radio/choice-a11y-text.ts +0 -61
- package/src/widgets/radio/choice-icon/choice-icon.tsx +0 -218
- package/src/widgets/radio/choice-icon/cross-out-line.tsx +0 -51
- package/src/widgets/radio/choice-icon/shared-styles.ts +0 -15
- package/src/widgets/radio/choice-none-above.tsx +0 -56
- package/src/widgets/radio/choice.jsx.fixture.ts +0 -45
- package/src/widgets/radio/choice.tsx +0 -359
- package/src/widgets/radio/focus-ring.tsx +0 -57
- package/src/widgets/radio/option-status.tsx +0 -98
- package/src/widgets/radio/util.test.ts +0 -18
- package/src/widgets/radio/util.ts +0 -31
- package/src/widgets/radio/widget.tsx +0 -520
- package/src/widgets/radio.ts +0 -157
- package/src/widgets/reaction-diagram.tsx +0 -144
- package/src/widgets/sequence.tsx +0 -142
- package/src/widgets/simulator.tsx +0 -829
- package/src/widgets/sorter.tsx +0 -155
- package/src/widgets/table.tsx +0 -393
- package/src/widgets/unit.tsx +0 -374
- package/src/widgets/video-transcript-link.test.tsx +0 -213
- package/src/widgets/video-transcript-link.tsx +0 -84
- package/src/widgets/video.tsx +0 -135
- package/src/widgets.ts +0 -489
- package/src/zoom.ts +0 -555
- package/tsconfig-build.json +0 -26
- package/tsconfig-build.tsbuildinfo +0 -1
|
@@ -1,2597 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @babel/no-invalid-this, react/no-unsafe, react/sort-comp */
|
|
2
|
-
import {number as knumber, point as kpoint} from "@khanacademy/kmath";
|
|
3
|
-
import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
4
|
-
import $ from "jquery";
|
|
5
|
-
import debounce from "lodash.debounce";
|
|
6
|
-
import * as React from "react";
|
|
7
|
-
import _ from "underscore";
|
|
8
|
-
|
|
9
|
-
import Graph from "../components/graph";
|
|
10
|
-
import Interactive2 from "../interactive2";
|
|
11
|
-
import WrappedLine from "../interactive2/wrapped-line";
|
|
12
|
-
import {Errors} from "../logging/log";
|
|
13
|
-
import {PerseusError} from "../perseus-error";
|
|
14
|
-
import Util from "../util";
|
|
15
|
-
import KhanColors from "../util/colors";
|
|
16
|
-
import {
|
|
17
|
-
angleMeasures,
|
|
18
|
-
ccw,
|
|
19
|
-
collinear,
|
|
20
|
-
intersects,
|
|
21
|
-
lawOfCosines,
|
|
22
|
-
magnitude,
|
|
23
|
-
rotate,
|
|
24
|
-
vector,
|
|
25
|
-
sign,
|
|
26
|
-
getLineEquation,
|
|
27
|
-
getLineIntersection,
|
|
28
|
-
canonicalSineCoefficients,
|
|
29
|
-
similar,
|
|
30
|
-
} from "../util/geometry";
|
|
31
|
-
import GraphUtils from "../util/graph-utils";
|
|
32
|
-
import {polar} from "../util/graphie";
|
|
33
|
-
import {getInteractiveBoxFromSizeClass} from "../util/sizing-utils";
|
|
34
|
-
|
|
35
|
-
import type {Coord} from "../interactive2/types";
|
|
36
|
-
import type {
|
|
37
|
-
PerseusGraphType,
|
|
38
|
-
PerseusGraphTypeAngle,
|
|
39
|
-
PerseusGraphTypePoint,
|
|
40
|
-
PerseusGraphTypePolygon,
|
|
41
|
-
PerseusGraphTypeSegment,
|
|
42
|
-
PerseusInteractiveGraphWidgetOptions,
|
|
43
|
-
} from "../perseus-types";
|
|
44
|
-
import type {PerseusScore, WidgetExports, WidgetProps} from "../types";
|
|
45
|
-
import type {
|
|
46
|
-
QuadraticCoefficient,
|
|
47
|
-
SineCoefficient,
|
|
48
|
-
Range,
|
|
49
|
-
} from "../util/geometry";
|
|
50
|
-
|
|
51
|
-
const {DeprecationMixin} = Util;
|
|
52
|
-
|
|
53
|
-
const TRASH_ICON_URI =
|
|
54
|
-
"https://ka-perseus-graphie.s3.amazonaws.com/b1452c0d79fd0f7ff4c3af9488474a0a0decb361.png";
|
|
55
|
-
|
|
56
|
-
const defaultBackgroundImage = {
|
|
57
|
-
url: null,
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const eq = Util.eq;
|
|
61
|
-
const deepEq = Util.deepEq;
|
|
62
|
-
|
|
63
|
-
const UNLIMITED = "unlimited" as const;
|
|
64
|
-
|
|
65
|
-
// Sample background image:
|
|
66
|
-
// https://ka-perseus-graphie.s3.amazonaws.com/29c1b0fcd17fe63df0f148fe357044d5d5c7d0bb.png
|
|
67
|
-
|
|
68
|
-
// default to defaultValue if actual is null or undefined
|
|
69
|
-
function defaultVal<T>(actual: T | null | undefined, defaultValue: T): T {
|
|
70
|
-
return actual == null ? defaultValue : actual;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Less than or approximately equal
|
|
74
|
-
function leq(a: any, b) {
|
|
75
|
-
return a < b || eq(a, b);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function capitalize(str) {
|
|
79
|
-
return str.replace(/(?:^|-)(.)/g, function (match, letter) {
|
|
80
|
-
return letter.toUpperCase();
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function numSteps(range: Range, step: number) {
|
|
85
|
-
return Math.floor((range[1] - range[0]) / step);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const deprecatedProps = {
|
|
89
|
-
showGraph: function (props) {
|
|
90
|
-
return {markings: props.showGraph ? "graph" : "none"};
|
|
91
|
-
},
|
|
92
|
-
} as const;
|
|
93
|
-
|
|
94
|
-
const _getShouldShowInstructions: (arg1: Props) => boolean = (props) => {
|
|
95
|
-
return (
|
|
96
|
-
_isClickToAddPoints(props) &&
|
|
97
|
-
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
98
|
-
(props.graph.coords == null || props.graph.coords.length === 0)
|
|
99
|
-
);
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const _isClickToAddPoints: (arg1: Props) => boolean = (props) => {
|
|
103
|
-
return (
|
|
104
|
-
(props.graph.type === "point" && props.graph.numPoints === UNLIMITED) ||
|
|
105
|
-
(props.graph.type === "polygon" && props.graph.numSides === UNLIMITED)
|
|
106
|
-
);
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const makeInvalidTypeError = (
|
|
110
|
-
functionName: string,
|
|
111
|
-
graphType: string,
|
|
112
|
-
): PerseusError => {
|
|
113
|
-
return new PerseusError(
|
|
114
|
-
`${functionName} called but current graph type is not a '${graphType}'`,
|
|
115
|
-
Errors.NotAllowed,
|
|
116
|
-
{metadata: {graphType}},
|
|
117
|
-
);
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
type RenderProps = PerseusInteractiveGraphWidgetOptions; // There's no transform function in exports
|
|
121
|
-
type Rubric = PerseusInteractiveGraphWidgetOptions;
|
|
122
|
-
type Props = WidgetProps<RenderProps, Rubric>;
|
|
123
|
-
type State = any;
|
|
124
|
-
type DefaultProps = {
|
|
125
|
-
labels: Props["labels"];
|
|
126
|
-
range: Props["range"];
|
|
127
|
-
step: Props["step"];
|
|
128
|
-
backgroundImage: Props["backgroundImage"];
|
|
129
|
-
markings: Props["markings"];
|
|
130
|
-
showTooltips: Props["showTooltips"];
|
|
131
|
-
showProtractor: Props["showProtractor"];
|
|
132
|
-
showRuler: Props["showRuler"];
|
|
133
|
-
rulerLabel: Props["rulerLabel"];
|
|
134
|
-
rulerTicks: Props["rulerTicks"];
|
|
135
|
-
graph: Props["graph"];
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
class InteractiveGraph extends React.Component<Props, State> {
|
|
139
|
-
angle: any | null | undefined;
|
|
140
|
-
circle: any | null | undefined;
|
|
141
|
-
graphie: any | null | undefined;
|
|
142
|
-
horizHairline: any | null | undefined;
|
|
143
|
-
line: any | null | undefined;
|
|
144
|
-
lines: ReadonlyArray<ReadonlyArray<Coord>> | null | undefined;
|
|
145
|
-
parabola: any | null | undefined;
|
|
146
|
-
pointA: any | null | undefined;
|
|
147
|
-
pointB: any | null | undefined;
|
|
148
|
-
pointC: any | null | undefined;
|
|
149
|
-
// @ts-expect-error - TS2564 - Property 'points' has no initializer and is not definitely assigned in the constructor.
|
|
150
|
-
points: Array<any>;
|
|
151
|
-
polygon: any | null | undefined;
|
|
152
|
-
// @ts-expect-error - TS2564 - Property 'shouldResetGraphie' has no initializer and is not definitely assigned in the constructor.
|
|
153
|
-
shouldResetGraphie: boolean;
|
|
154
|
-
sinusoid: any | null | undefined;
|
|
155
|
-
trashCan: any | null | undefined;
|
|
156
|
-
vertHairline: any | null | undefined;
|
|
157
|
-
|
|
158
|
-
static defaultProps: DefaultProps = {
|
|
159
|
-
labels: ["x", "y"],
|
|
160
|
-
range: [
|
|
161
|
-
[-10, 10],
|
|
162
|
-
[-10, 10],
|
|
163
|
-
],
|
|
164
|
-
step: [1, 1],
|
|
165
|
-
backgroundImage: defaultBackgroundImage,
|
|
166
|
-
markings: "graph",
|
|
167
|
-
showTooltips: false,
|
|
168
|
-
showProtractor: false,
|
|
169
|
-
showRuler: false,
|
|
170
|
-
rulerLabel: "",
|
|
171
|
-
rulerTicks: 10,
|
|
172
|
-
graph: {
|
|
173
|
-
type: "linear",
|
|
174
|
-
},
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
state: State = {
|
|
178
|
-
shouldShowInstructions: _getShouldShowInstructions(this.props),
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
// TODO(jangmi, CP-3288): Remove usage of `UNSAFE_componentWillMount`
|
|
182
|
-
UNSAFE_componentWillMount() {
|
|
183
|
-
DeprecationMixin.UNSAFE_componentWillMount.call(this);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
componentDidMount() {
|
|
187
|
-
// eslint-disable-next-line react/no-string-refs
|
|
188
|
-
// @ts-expect-error - TS2339 - Property 'graphie' does not exist on type 'ReactInstance'.
|
|
189
|
-
this.setGraphie(this.refs.graph.graphie());
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
UNSAFE_componentWillReceiveProps(nextProps: Props) {
|
|
193
|
-
if (this.isClickToAddPoints() !== this.isClickToAddPoints(nextProps)) {
|
|
194
|
-
this.shouldResetGraphie = true;
|
|
195
|
-
this.setState({
|
|
196
|
-
shouldShowInstructions: _getShouldShowInstructions(nextProps),
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (
|
|
201
|
-
this.props.backgroundImage?.url !==
|
|
202
|
-
nextProps.backgroundImage?.url ||
|
|
203
|
-
this.props.backgroundImage !== nextProps.backgroundImage ||
|
|
204
|
-
this.props.containerSizeClass !== nextProps.containerSizeClass
|
|
205
|
-
) {
|
|
206
|
-
this.shouldResetGraphie = true;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
componentDidUpdate(prevProps: Props, prevState: State) {
|
|
211
|
-
// TODO(jeremy): This feels underspecified. For example, if the props
|
|
212
|
-
// change the coords of a segment graph, but not the numSegments, we'd
|
|
213
|
-
// not count that as an update.
|
|
214
|
-
const oldType = prevProps.graph.type;
|
|
215
|
-
const newType = this.props.graph.type;
|
|
216
|
-
if (
|
|
217
|
-
oldType !== newType ||
|
|
218
|
-
(prevProps.graph.type === "angle" &&
|
|
219
|
-
this.props.graph.type === "angle" &&
|
|
220
|
-
this.areAngleGraphsEqual(prevProps.graph, this.props.graph)) ||
|
|
221
|
-
(prevProps.graph.type === "point" &&
|
|
222
|
-
this.props.graph.type === "point" &&
|
|
223
|
-
this.arePointGraphsEqual(prevProps.graph, this.props.graph)) ||
|
|
224
|
-
(prevProps.graph.type === "polygon" &&
|
|
225
|
-
this.props.graph.type === "polygon" &&
|
|
226
|
-
this.arePolygonGraphsEqual(
|
|
227
|
-
prevProps.graph,
|
|
228
|
-
this.props.graph,
|
|
229
|
-
)) ||
|
|
230
|
-
(prevProps.graph.type === "segment" &&
|
|
231
|
-
this.props.graph.type === "segment" &&
|
|
232
|
-
this.areSegmentGraphsEqual(prevProps.graph, this.props.graph))
|
|
233
|
-
) {
|
|
234
|
-
this["remove" + capitalize(oldType) + "Controls"]();
|
|
235
|
-
this["add" + capitalize(newType) + "Controls"]();
|
|
236
|
-
}
|
|
237
|
-
if (this.shouldResetGraphie) {
|
|
238
|
-
this.resetGraphie();
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
_getShouldShowInstructions: (arg1?: Props | null | undefined) => boolean = (
|
|
243
|
-
props,
|
|
244
|
-
) => {
|
|
245
|
-
props = props || this.props;
|
|
246
|
-
return (
|
|
247
|
-
this.isClickToAddPoints(props) &&
|
|
248
|
-
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
249
|
-
(props.graph.coords == null || props.graph.coords.length === 0)
|
|
250
|
-
);
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
deprecatedProps: any = deprecatedProps;
|
|
254
|
-
|
|
255
|
-
setGraphie: (arg1: any) => void = (newGraphie) => {
|
|
256
|
-
this.graphie = newGraphie;
|
|
257
|
-
this.setupGraphie();
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
handleAddPointsMouseDown: (arg1: Coord) => void = (coord) => {
|
|
261
|
-
// This function should only be called when this.isClickToAddPoints()
|
|
262
|
-
// is true
|
|
263
|
-
if (!this.isClickToAddPoints()) {
|
|
264
|
-
throw new PerseusError(
|
|
265
|
-
"handleAddPointsClick should not be registered" +
|
|
266
|
-
"when isClickToAddPoints() is false",
|
|
267
|
-
Errors.NotAllowed,
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
if (!this.isCoordInTrash(coord)) {
|
|
271
|
-
let point;
|
|
272
|
-
|
|
273
|
-
if (this.props.graph.type === "point") {
|
|
274
|
-
point = this.createPointForPointsType(
|
|
275
|
-
coord,
|
|
276
|
-
this.points.length,
|
|
277
|
-
);
|
|
278
|
-
if (!point.constrain()) {
|
|
279
|
-
point.remove();
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
this.points.push(point);
|
|
283
|
-
|
|
284
|
-
// interactive2 allows us to grab the point
|
|
285
|
-
const idx = this.points.length - 1;
|
|
286
|
-
this.points[idx].grab(coord);
|
|
287
|
-
|
|
288
|
-
this.updateCoordsFromPoints();
|
|
289
|
-
} else if (this.props.graph.type === "polygon") {
|
|
290
|
-
if (this.polygon?.closed()) {
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
point = this.createPointForPolygonType(
|
|
294
|
-
coord,
|
|
295
|
-
this.points.length,
|
|
296
|
-
);
|
|
297
|
-
this.points.push(point);
|
|
298
|
-
|
|
299
|
-
const idx = this.points.length - 1;
|
|
300
|
-
this.points[idx].grab(coord);
|
|
301
|
-
|
|
302
|
-
// We don't call updateCoordsFromPoints for
|
|
303
|
-
// polygons, since the polygon won't be
|
|
304
|
-
// closed yet.
|
|
305
|
-
this.updatePolygon();
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
this.setState({
|
|
309
|
-
shouldShowInstructions: false,
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
resetGraphie: () => void = () => {
|
|
315
|
-
this.shouldResetGraphie = false;
|
|
316
|
-
this.parabola = null;
|
|
317
|
-
this.sinusoid = null;
|
|
318
|
-
// eslint-disable-next-line react/no-string-refs
|
|
319
|
-
// @ts-expect-error - TS2339 - Property 'reset' does not exist on type 'ReactInstance'.
|
|
320
|
-
this.refs.graph.reset();
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
setupGraphie: () => void = () => {
|
|
324
|
-
this.setTrashCanVisibility(0);
|
|
325
|
-
if (this.isClickToAddPoints()) {
|
|
326
|
-
this.setTrashCanVisibility(0.5);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
if (this.props.apiOptions.isMobile) {
|
|
330
|
-
this.horizHairline = new WrappedLine(this.graphie, [0, 0], [0, 0], {
|
|
331
|
-
normalStyle: {
|
|
332
|
-
strokeWidth: 1,
|
|
333
|
-
},
|
|
334
|
-
});
|
|
335
|
-
this.horizHairline.attr({
|
|
336
|
-
stroke: KhanColors.INTERACTIVE,
|
|
337
|
-
});
|
|
338
|
-
this.horizHairline?.hide();
|
|
339
|
-
|
|
340
|
-
this.vertHairline = new WrappedLine(this.graphie, [0, 0], [0, 0], {
|
|
341
|
-
normalStyle: {
|
|
342
|
-
strokeWidth: 1,
|
|
343
|
-
},
|
|
344
|
-
});
|
|
345
|
-
this.vertHairline.attr({
|
|
346
|
-
stroke: KhanColors.INTERACTIVE,
|
|
347
|
-
});
|
|
348
|
-
this.vertHairline?.hide();
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
const type = this.props.graph.type;
|
|
352
|
-
this["add" + capitalize(type) + "Controls"]();
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
showHairlines: (arg1: Coord) => void = (point) => {
|
|
356
|
-
if (this.props.apiOptions.isMobile && this.props.markings !== "none") {
|
|
357
|
-
// Hairlines are already initialized when the graph is loaded, so
|
|
358
|
-
// here we just move them to the updated location and make them
|
|
359
|
-
// visible.
|
|
360
|
-
this.horizHairline?.moveTo(
|
|
361
|
-
[this.props.range[0][0], point[1]],
|
|
362
|
-
[this.props.range[0][1], point[1]],
|
|
363
|
-
);
|
|
364
|
-
|
|
365
|
-
this.horizHairline?.show();
|
|
366
|
-
|
|
367
|
-
this.vertHairline?.moveTo(
|
|
368
|
-
[point[0], this.props.range[1][0]],
|
|
369
|
-
[point[0], this.props.range[1][1]],
|
|
370
|
-
);
|
|
371
|
-
|
|
372
|
-
this.vertHairline?.show();
|
|
373
|
-
}
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
hideHairlines: () => void = () => {
|
|
377
|
-
if (this.props.apiOptions.isMobile) {
|
|
378
|
-
this.horizHairline?.hide();
|
|
379
|
-
this.vertHairline?.hide();
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
setTrashCanVisibility: (arg1: number) => void = (opacity) => {
|
|
384
|
-
const graphie = this.graphie;
|
|
385
|
-
|
|
386
|
-
if (knumber.equal(opacity, 0)) {
|
|
387
|
-
if (this.trashCan) {
|
|
388
|
-
this.trashCan.remove();
|
|
389
|
-
this.trashCan = null;
|
|
390
|
-
}
|
|
391
|
-
} else if (!this.props.apiOptions.isMobile) {
|
|
392
|
-
// Only if trash tooltips are not being used, we initialize the old
|
|
393
|
-
// trash can area.
|
|
394
|
-
if (!this.trashCan) {
|
|
395
|
-
this.trashCan = graphie?.raphael?.image(
|
|
396
|
-
TRASH_ICON_URI,
|
|
397
|
-
graphie.xpixels - 40,
|
|
398
|
-
graphie.ypixels - 40,
|
|
399
|
-
40,
|
|
400
|
-
40,
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
this.trashCan?.attr({
|
|
404
|
-
opacity: opacity,
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
isClickToAddPoints: (arg1?: Props | null | undefined) => boolean = (
|
|
410
|
-
props,
|
|
411
|
-
) => {
|
|
412
|
-
props = props || this.props;
|
|
413
|
-
return _isClickToAddPoints(props);
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
areAngleGraphsEqual(
|
|
417
|
-
prevGraph: PerseusGraphTypeAngle,
|
|
418
|
-
currentGraph: PerseusGraphTypeAngle,
|
|
419
|
-
): boolean {
|
|
420
|
-
return (
|
|
421
|
-
prevGraph.allowReflexAngles !== currentGraph.allowReflexAngles ||
|
|
422
|
-
prevGraph.angleOffsetDeg !== currentGraph.angleOffsetDeg ||
|
|
423
|
-
prevGraph.snapDegrees !== currentGraph.snapDegrees
|
|
424
|
-
);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
arePointGraphsEqual(
|
|
428
|
-
prevGraph: PerseusGraphTypePoint,
|
|
429
|
-
currentGraph: PerseusGraphTypePoint,
|
|
430
|
-
): boolean {
|
|
431
|
-
return prevGraph.numPoints !== currentGraph.numPoints;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
arePolygonGraphsEqual(
|
|
435
|
-
prevGraph: PerseusGraphTypePolygon,
|
|
436
|
-
currentGraph: PerseusGraphTypePolygon,
|
|
437
|
-
): boolean {
|
|
438
|
-
return (
|
|
439
|
-
prevGraph.numSides !== currentGraph.numSides ||
|
|
440
|
-
prevGraph.showAngles !== currentGraph.showAngles ||
|
|
441
|
-
prevGraph.showSides !== currentGraph.showSides ||
|
|
442
|
-
prevGraph.snapTo !== currentGraph.snapTo
|
|
443
|
-
);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
areSegmentGraphsEqual(
|
|
447
|
-
prevGraph: PerseusGraphTypeSegment,
|
|
448
|
-
currentGraph: PerseusGraphTypeSegment,
|
|
449
|
-
): boolean {
|
|
450
|
-
return prevGraph.numSegments !== currentGraph.numSegments;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
_lineStroke: () => {
|
|
454
|
-
["stroke-width"]?: number;
|
|
455
|
-
} = () => {
|
|
456
|
-
// This should probably use: this.props.apiOptions.isMobile
|
|
457
|
-
// @ts-expect-error - TS2339 - Property 'isMobile' does not exist on type 'Readonly<Props> & Readonly<{ children?: ReactNode; }>'.
|
|
458
|
-
return this.props.isMobile ? {"stroke-width": 3} : {};
|
|
459
|
-
};
|
|
460
|
-
|
|
461
|
-
addLine: (arg1: string) => void = (type) => {
|
|
462
|
-
const self = this;
|
|
463
|
-
const graphie = self.graphie;
|
|
464
|
-
const coords = InteractiveGraph.getLineCoords(
|
|
465
|
-
self.props.graph,
|
|
466
|
-
self.props,
|
|
467
|
-
);
|
|
468
|
-
|
|
469
|
-
const points = (self.points = _.map(coords, (coord) => {
|
|
470
|
-
return Interactive2.addMaybeMobileMovablePoint(this, {
|
|
471
|
-
coord: coord,
|
|
472
|
-
constraints: [
|
|
473
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
474
|
-
Interactive2.MovablePoint.constraints.bound(),
|
|
475
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
476
|
-
Interactive2.MovablePoint.constraints.snap(),
|
|
477
|
-
],
|
|
478
|
-
onMove: () => {
|
|
479
|
-
const graph = _.extend({}, self.props.graph, {
|
|
480
|
-
coords: _.invoke(points, "coord"),
|
|
481
|
-
});
|
|
482
|
-
self.onChange({graph: graph});
|
|
483
|
-
},
|
|
484
|
-
});
|
|
485
|
-
}));
|
|
486
|
-
|
|
487
|
-
const lineConfig = {
|
|
488
|
-
points: points,
|
|
489
|
-
static: true,
|
|
490
|
-
normalStyle: {
|
|
491
|
-
stroke: this.props.apiOptions.isMobile
|
|
492
|
-
? KhanColors.BLUE_C
|
|
493
|
-
: KhanColors.INTERACTIVE,
|
|
494
|
-
...this._lineStroke(),
|
|
495
|
-
},
|
|
496
|
-
} as const;
|
|
497
|
-
|
|
498
|
-
if (type === "line") {
|
|
499
|
-
// @ts-expect-error - TS2339 - Property 'extendLine' does not exist on type '{ readonly points: any[]; readonly static: true; readonly normalStyle: { readonly "stroke-width"?: number | undefined; readonly stroke: "#71B307" | "#63D9EA"; }; }'.
|
|
500
|
-
lineConfig.extendLine = true;
|
|
501
|
-
} else if (type === "ray") {
|
|
502
|
-
// @ts-expect-error - TS2339 - Property 'extendRay' does not exist on type '{ readonly points: any[]; readonly static: true; readonly normalStyle: { readonly "stroke-width"?: number | undefined; readonly stroke: "#71B307" | "#63D9EA"; }; }'.
|
|
503
|
-
lineConfig.extendRay = true;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
self.line = Interactive2.addMovableLine(graphie, lineConfig);
|
|
507
|
-
|
|
508
|
-
// A and B can't be in the same place
|
|
509
|
-
points[0].listen("constraints", "isLine", (coord) => {
|
|
510
|
-
return !kpoint.equal(coord, points[1].coord());
|
|
511
|
-
});
|
|
512
|
-
points[1].listen("constraints", "isLine", (coord) => {
|
|
513
|
-
return !kpoint.equal(coord, points[0].coord());
|
|
514
|
-
});
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
removeLine: () => void = () => {
|
|
518
|
-
_.invoke(this.points, "remove");
|
|
519
|
-
this.line?.remove();
|
|
520
|
-
};
|
|
521
|
-
|
|
522
|
-
addLinearControls: () => void = () => {
|
|
523
|
-
this.addLine("line");
|
|
524
|
-
};
|
|
525
|
-
|
|
526
|
-
removeLinearControls: () => void = () => {
|
|
527
|
-
this.removeLine();
|
|
528
|
-
};
|
|
529
|
-
|
|
530
|
-
addQuadraticControls: () => void = () => {
|
|
531
|
-
if (this.props.graph.type !== "quadratic") {
|
|
532
|
-
throw makeInvalidTypeError("addQuadraticControls", "quadratic");
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
let coords = this.props.graph.coords;
|
|
536
|
-
if (!coords) {
|
|
537
|
-
coords = InteractiveGraph.defaultQuadraticCoords(this.props);
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
const onMoveHandler = () => {
|
|
541
|
-
const graph = _.extend({}, this.props.graph, {
|
|
542
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2531 - Object is possibly 'null'. | TS2531 - Object is possibly 'null'.
|
|
543
|
-
coords: [pointA.coord(), pointB.coord(), pointC.coord()],
|
|
544
|
-
});
|
|
545
|
-
this.onChange({graph});
|
|
546
|
-
this.updateQuadratic();
|
|
547
|
-
};
|
|
548
|
-
|
|
549
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
550
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
551
|
-
// to be executed synchronously, which causes this issue.
|
|
552
|
-
let pointA = null;
|
|
553
|
-
pointA = this.pointA = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
554
|
-
coord: coords[0],
|
|
555
|
-
constraints: [
|
|
556
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
557
|
-
Interactive2.MovablePoint.constraints.bound(),
|
|
558
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
559
|
-
Interactive2.MovablePoint.constraints.snap(),
|
|
560
|
-
(coord) => {
|
|
561
|
-
return (
|
|
562
|
-
!pointA ||
|
|
563
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
564
|
-
(coord[0] !== pointB.coord()[0] &&
|
|
565
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
566
|
-
coord[0] !== pointC.coord()[0])
|
|
567
|
-
);
|
|
568
|
-
},
|
|
569
|
-
],
|
|
570
|
-
onMove: onMoveHandler,
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
574
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
575
|
-
// to be executed synchronously, which causes this issue.
|
|
576
|
-
let pointB = null;
|
|
577
|
-
pointB = this.pointB = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
578
|
-
coord: coords[1],
|
|
579
|
-
constraints: [
|
|
580
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
581
|
-
Interactive2.MovablePoint.constraints.bound(),
|
|
582
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
583
|
-
Interactive2.MovablePoint.constraints.snap(),
|
|
584
|
-
(coord: any) => {
|
|
585
|
-
return (
|
|
586
|
-
!pointB ||
|
|
587
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
588
|
-
(coord[0] !== pointA.coord()[0] &&
|
|
589
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
590
|
-
coord[0] !== pointC.coord()[0])
|
|
591
|
-
);
|
|
592
|
-
},
|
|
593
|
-
],
|
|
594
|
-
onMove: onMoveHandler,
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
598
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
599
|
-
// to be executed synchronously, which causes this issue.
|
|
600
|
-
let pointC = null;
|
|
601
|
-
pointC = this.pointC = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
602
|
-
coord: coords[2],
|
|
603
|
-
constraints: [
|
|
604
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
605
|
-
Interactive2.MovablePoint.constraints.bound(),
|
|
606
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
607
|
-
Interactive2.MovablePoint.constraints.snap(),
|
|
608
|
-
(coord: any) => {
|
|
609
|
-
return (
|
|
610
|
-
!pointC ||
|
|
611
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
612
|
-
(coord[0] !== pointA.coord()[0] &&
|
|
613
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
614
|
-
coord[0] !== pointB.coord()[0])
|
|
615
|
-
);
|
|
616
|
-
},
|
|
617
|
-
],
|
|
618
|
-
onMove: onMoveHandler,
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
this.updateQuadratic();
|
|
622
|
-
};
|
|
623
|
-
|
|
624
|
-
updateQuadratic: () => void = () => {
|
|
625
|
-
const coeffs = InteractiveGraph.getCurrentQuadraticCoefficients(
|
|
626
|
-
this.props,
|
|
627
|
-
);
|
|
628
|
-
if (!coeffs) {
|
|
629
|
-
return;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// Extract coefficients the parabola
|
|
633
|
-
const a = coeffs[0];
|
|
634
|
-
const b = coeffs[1];
|
|
635
|
-
const c = coeffs[2];
|
|
636
|
-
|
|
637
|
-
// Plot and style
|
|
638
|
-
if (this.parabola) {
|
|
639
|
-
const path = this.graphie?.svgParabolaPath(a, b, c);
|
|
640
|
-
this.parabola?.attr({path: path});
|
|
641
|
-
} else {
|
|
642
|
-
this.parabola = this.graphie?.parabola(a, b, c);
|
|
643
|
-
this.parabola?.attr({
|
|
644
|
-
stroke: this.props.apiOptions.isMobile
|
|
645
|
-
? KhanColors.BLUE_C
|
|
646
|
-
: KhanColors.DYNAMIC,
|
|
647
|
-
...this._lineStroke(),
|
|
648
|
-
});
|
|
649
|
-
this.parabola?.toBack();
|
|
650
|
-
}
|
|
651
|
-
};
|
|
652
|
-
|
|
653
|
-
removeQuadraticControls: () => void = () => {
|
|
654
|
-
this.pointA?.remove();
|
|
655
|
-
this.pointB?.remove();
|
|
656
|
-
this.pointC?.remove();
|
|
657
|
-
if (this.parabola) {
|
|
658
|
-
this.parabola.remove();
|
|
659
|
-
this.parabola = null;
|
|
660
|
-
}
|
|
661
|
-
};
|
|
662
|
-
|
|
663
|
-
addSinusoidControls: () => void = () => {
|
|
664
|
-
if (this.props.graph.type !== "sinusoid") {
|
|
665
|
-
throw makeInvalidTypeError("addSinusoidControls", "sinusoid");
|
|
666
|
-
}
|
|
667
|
-
let coords = this.props.graph.coords;
|
|
668
|
-
if (!coords) {
|
|
669
|
-
coords = InteractiveGraph.defaultSinusoidCoords(this.props);
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
const onMoveHandler = () => {
|
|
673
|
-
const graph = _.extend({}, this.props.graph, {
|
|
674
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
675
|
-
coords: [pointA.coord(), pointB.coord()],
|
|
676
|
-
});
|
|
677
|
-
this.onChange({graph: graph});
|
|
678
|
-
this.updateSinusoid();
|
|
679
|
-
};
|
|
680
|
-
|
|
681
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
682
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
683
|
-
// to be executed synchronously, which causes this issue.
|
|
684
|
-
let pointA = null;
|
|
685
|
-
pointA = this.pointA = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
686
|
-
coord: coords[0],
|
|
687
|
-
constraints: [
|
|
688
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
689
|
-
Interactive2.MovablePoint.constraints.bound(),
|
|
690
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
691
|
-
Interactive2.MovablePoint.constraints.snap(),
|
|
692
|
-
(coord: any) => {
|
|
693
|
-
return !pointA || coord[0] !== pointB.coord()[0];
|
|
694
|
-
},
|
|
695
|
-
],
|
|
696
|
-
onMove: onMoveHandler,
|
|
697
|
-
});
|
|
698
|
-
|
|
699
|
-
const pointB = (this.pointB = Interactive2.addMaybeMobileMovablePoint(
|
|
700
|
-
this,
|
|
701
|
-
{
|
|
702
|
-
coord: coords[1],
|
|
703
|
-
constraints: [
|
|
704
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
705
|
-
Interactive2.MovablePoint.constraints.bound(),
|
|
706
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
707
|
-
Interactive2.MovablePoint.constraints.snap(),
|
|
708
|
-
(coord: any) => {
|
|
709
|
-
// @ts-expect-error - TS2339 - Property 'coord' does not exist on type 'never'.
|
|
710
|
-
return !pointA || coord[0] !== pointA.coord()[0];
|
|
711
|
-
},
|
|
712
|
-
],
|
|
713
|
-
onMove: onMoveHandler,
|
|
714
|
-
},
|
|
715
|
-
));
|
|
716
|
-
|
|
717
|
-
this.updateSinusoid();
|
|
718
|
-
};
|
|
719
|
-
|
|
720
|
-
updateSinusoid: () => void = () => {
|
|
721
|
-
const coeffs = InteractiveGraph.getCurrentSinusoidCoefficients(
|
|
722
|
-
this.props,
|
|
723
|
-
);
|
|
724
|
-
if (!coeffs) {
|
|
725
|
-
return;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
const a = coeffs[0];
|
|
729
|
-
const b = coeffs[1];
|
|
730
|
-
const c = coeffs[2];
|
|
731
|
-
const d = coeffs[3];
|
|
732
|
-
|
|
733
|
-
// Plot and style
|
|
734
|
-
if (this.sinusoid) {
|
|
735
|
-
const path = this.graphie?.svgSinusoidPath(a, b, c, d);
|
|
736
|
-
this.sinusoid?.attr({path: path});
|
|
737
|
-
} else {
|
|
738
|
-
this.sinusoid = this.graphie?.sinusoid(a, b, c, d);
|
|
739
|
-
this.sinusoid?.attr({
|
|
740
|
-
stroke: this.props.apiOptions.isMobile
|
|
741
|
-
? KhanColors.BLUE_C
|
|
742
|
-
: KhanColors.DYNAMIC,
|
|
743
|
-
...this._lineStroke(),
|
|
744
|
-
});
|
|
745
|
-
this.sinusoid?.toBack();
|
|
746
|
-
}
|
|
747
|
-
};
|
|
748
|
-
|
|
749
|
-
removeSinusoidControls: () => void = () => {
|
|
750
|
-
this.pointA?.remove();
|
|
751
|
-
this.pointB?.remove();
|
|
752
|
-
if (this.sinusoid) {
|
|
753
|
-
this.sinusoid.remove();
|
|
754
|
-
this.sinusoid = null;
|
|
755
|
-
}
|
|
756
|
-
};
|
|
757
|
-
|
|
758
|
-
addCircleControls: () => void = () => {
|
|
759
|
-
const graphie = this.graphie;
|
|
760
|
-
const minSnap = _.min(graphie.snap);
|
|
761
|
-
|
|
762
|
-
const circle = (this.circle = graphie?.addCircleGraph({
|
|
763
|
-
// @ts-expect-error - TS2339 - Property 'center' does not exist on type 'PerseusGraphType'.
|
|
764
|
-
center: this.props.graph.center || [0, 0],
|
|
765
|
-
// @ts-expect-error - TS2339 - Property 'radius' does not exist on type 'PerseusGraphType'.
|
|
766
|
-
radius: this.props.graph.radius || _.min(this.props.step),
|
|
767
|
-
snapX: graphie.snap[0],
|
|
768
|
-
snapY: graphie.snap[1],
|
|
769
|
-
minRadius: minSnap,
|
|
770
|
-
snapRadius: minSnap,
|
|
771
|
-
}));
|
|
772
|
-
|
|
773
|
-
$(circle).on("move", () => {
|
|
774
|
-
const graph = _.extend({}, this.props.graph, {
|
|
775
|
-
center: circle.center,
|
|
776
|
-
radius: circle.radius,
|
|
777
|
-
});
|
|
778
|
-
this.onChange({graph: graph});
|
|
779
|
-
});
|
|
780
|
-
};
|
|
781
|
-
|
|
782
|
-
removeCircleControls: () => void = () => {
|
|
783
|
-
this.circle?.remove();
|
|
784
|
-
};
|
|
785
|
-
|
|
786
|
-
addLinearSystemControls: () => void = () => {
|
|
787
|
-
const graphie = this.graphie;
|
|
788
|
-
const coords = InteractiveGraph.getLinearSystemCoords(
|
|
789
|
-
this.props.graph,
|
|
790
|
-
this.props,
|
|
791
|
-
);
|
|
792
|
-
|
|
793
|
-
const segmentColors = [KhanColors.INTERACTIVE, KhanColors.GREEN];
|
|
794
|
-
const points = (this.points = _.map(
|
|
795
|
-
coords,
|
|
796
|
-
(segmentCoords, segmentIndex) => {
|
|
797
|
-
// NOTE(jeresig): This code attempts to access the variable
|
|
798
|
-
// while it's still being defined, it should be refactored!
|
|
799
|
-
// The callbacks appear to be executed synchronously, which
|
|
800
|
-
// causes this issue.
|
|
801
|
-
const segmentPoints: Array<any> = [];
|
|
802
|
-
|
|
803
|
-
for (let i = 0; i < segmentCoords.length; i += 1) {
|
|
804
|
-
const coord = segmentCoords[i];
|
|
805
|
-
segmentPoints.push(
|
|
806
|
-
Interactive2.addMaybeMobileMovablePoint(this, {
|
|
807
|
-
coord: coord,
|
|
808
|
-
constraints: [
|
|
809
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
810
|
-
Interactive2.MovablePoint.constraints.bound(),
|
|
811
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
812
|
-
Interactive2.MovablePoint.constraints.snap(),
|
|
813
|
-
(coord) => {
|
|
814
|
-
const otherSegment = segmentPoints[1 - i];
|
|
815
|
-
if (!otherSegment) {
|
|
816
|
-
// segment hasn't been defined yet
|
|
817
|
-
// because we're still creating them
|
|
818
|
-
return;
|
|
819
|
-
}
|
|
820
|
-
return !kpoint.equal(
|
|
821
|
-
coord,
|
|
822
|
-
otherSegment.coord(),
|
|
823
|
-
);
|
|
824
|
-
},
|
|
825
|
-
],
|
|
826
|
-
onMove: () => {
|
|
827
|
-
const graph = _.extend({}, this.props.graph, {
|
|
828
|
-
coords: _.map(this.points, (segment) =>
|
|
829
|
-
_.invoke(segment, "coord"),
|
|
830
|
-
),
|
|
831
|
-
});
|
|
832
|
-
this.onChange({graph: graph});
|
|
833
|
-
},
|
|
834
|
-
normalStyle: {
|
|
835
|
-
fill: segmentColors[segmentIndex],
|
|
836
|
-
},
|
|
837
|
-
highlightStyle: {
|
|
838
|
-
fill: segmentColors[segmentIndex],
|
|
839
|
-
},
|
|
840
|
-
}),
|
|
841
|
-
);
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
return segmentPoints;
|
|
845
|
-
},
|
|
846
|
-
));
|
|
847
|
-
|
|
848
|
-
this.lines = _.map(points, (segmentPoints, segmentIndex) => {
|
|
849
|
-
return Interactive2.addMovableLine(graphie, {
|
|
850
|
-
points: segmentPoints,
|
|
851
|
-
static: true,
|
|
852
|
-
extendLine: true,
|
|
853
|
-
normalStyle: {
|
|
854
|
-
stroke: segmentColors[segmentIndex],
|
|
855
|
-
},
|
|
856
|
-
});
|
|
857
|
-
});
|
|
858
|
-
};
|
|
859
|
-
|
|
860
|
-
removeLinearSystemControls: () => void = () => {
|
|
861
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
862
|
-
_.invoke(this.lines, "remove");
|
|
863
|
-
_.map(this.points, (segment) => _.invoke(segment, "remove"));
|
|
864
|
-
};
|
|
865
|
-
|
|
866
|
-
isCoordInTrash: (arg1: Coord) => boolean = (coord) => {
|
|
867
|
-
if (this.props.apiOptions.isMobile) {
|
|
868
|
-
return false;
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
const graphie = this.graphie;
|
|
872
|
-
const screenPoint = graphie?.scalePoint(coord);
|
|
873
|
-
return (
|
|
874
|
-
screenPoint[0] >= graphie.xpixels - 40 &&
|
|
875
|
-
screenPoint[1] >= graphie.ypixels - 40
|
|
876
|
-
);
|
|
877
|
-
};
|
|
878
|
-
|
|
879
|
-
createPointForPointsType: (arg1: Coord, arg2: number) => any = (
|
|
880
|
-
coord,
|
|
881
|
-
i,
|
|
882
|
-
) => {
|
|
883
|
-
const self = this;
|
|
884
|
-
|
|
885
|
-
const remove = () => {
|
|
886
|
-
self.points = _.filter(self.points, function (pt) {
|
|
887
|
-
return pt !== point;
|
|
888
|
-
});
|
|
889
|
-
// update the correct answer box
|
|
890
|
-
self.updateCoordsFromPoints();
|
|
891
|
-
|
|
892
|
-
// remove this movablePoint from graphie.
|
|
893
|
-
// we wait to do this until we're not inside of
|
|
894
|
-
// said point's onMoveEnd method so its state is
|
|
895
|
-
// consistent throughout this method call
|
|
896
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
897
|
-
// @ts-expect-error - TS2531 - Object is possibly 'null'.
|
|
898
|
-
setTimeout(point.remove.bind(point), 0); // eslint-disable-line no-restricted-syntax
|
|
899
|
-
};
|
|
900
|
-
|
|
901
|
-
// NOTE(jeresig): This code attempts to access the variable while it's
|
|
902
|
-
// still being defined, it should be refactored! The callbacks appear
|
|
903
|
-
// to be executed synchronously, which causes this issue.
|
|
904
|
-
let point = null;
|
|
905
|
-
point = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
906
|
-
coord: coord,
|
|
907
|
-
constraints: [
|
|
908
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
909
|
-
Interactive2.MovablePoint.constraints.bound(),
|
|
910
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
911
|
-
Interactive2.MovablePoint.constraints.snap(),
|
|
912
|
-
function (coord) {
|
|
913
|
-
// TODO(jack): There ought to be a
|
|
914
|
-
// MovablePoint.constraints.avoid
|
|
915
|
-
// default that lets you do things like this
|
|
916
|
-
return _.all(self.points, function (pt) {
|
|
917
|
-
return point === pt || !kpoint.equal(coord, pt.coord());
|
|
918
|
-
});
|
|
919
|
-
},
|
|
920
|
-
],
|
|
921
|
-
onMoveStart: function () {
|
|
922
|
-
if (self.isClickToAddPoints()) {
|
|
923
|
-
self.setTrashCanVisibility(1);
|
|
924
|
-
}
|
|
925
|
-
},
|
|
926
|
-
onMove: self.updateCoordsFromPoints,
|
|
927
|
-
onMoveEnd: function (coord) {
|
|
928
|
-
if (self.isClickToAddPoints()) {
|
|
929
|
-
if (self.isCoordInTrash(coord)) {
|
|
930
|
-
remove();
|
|
931
|
-
}
|
|
932
|
-
// In case we mouseup'd off the graphie and that
|
|
933
|
-
// stopped the move (in which case, we might not
|
|
934
|
-
// be in isCoordInTrash()
|
|
935
|
-
self.setTrashCanVisibility(0.5);
|
|
936
|
-
}
|
|
937
|
-
},
|
|
938
|
-
...(this.props.apiOptions.isMobile && self.isClickToAddPoints()
|
|
939
|
-
? {onRemove: remove}
|
|
940
|
-
: {}),
|
|
941
|
-
});
|
|
942
|
-
|
|
943
|
-
return point;
|
|
944
|
-
};
|
|
945
|
-
|
|
946
|
-
removePoint: (arg1: Coord) => number | null | undefined = (point) => {
|
|
947
|
-
let index = null;
|
|
948
|
-
this.points = _.filter(this.points, function (pt, i) {
|
|
949
|
-
if (pt === point) {
|
|
950
|
-
// @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'null'.
|
|
951
|
-
index = i;
|
|
952
|
-
return false;
|
|
953
|
-
}
|
|
954
|
-
return true;
|
|
955
|
-
});
|
|
956
|
-
return index;
|
|
957
|
-
};
|
|
958
|
-
|
|
959
|
-
createPointForPolygonType: (arg1: Coord, arg2: number) => any = (
|
|
960
|
-
coord,
|
|
961
|
-
i,
|
|
962
|
-
) => {
|
|
963
|
-
if (this.props.graph.type !== "polygon") {
|
|
964
|
-
throw makeInvalidTypeError("createPointForPolygonType", "polygon");
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
// TODO(alex): check against "grid" instead, use constants
|
|
968
|
-
const snapToGrid = !_.contains(
|
|
969
|
-
["angles", "sides"],
|
|
970
|
-
this.props.graph.snapTo,
|
|
971
|
-
);
|
|
972
|
-
|
|
973
|
-
// Index relative to current point -> absolute index
|
|
974
|
-
// NOTE: This does not work when isClickToAddPoints() == true,
|
|
975
|
-
// as `i` can be changed by dragging a point to the trash
|
|
976
|
-
// Currently this function is only called when !isClickToAddPoints()
|
|
977
|
-
const rel = (j) => {
|
|
978
|
-
return (i + j + this.points.length) % this.points.length;
|
|
979
|
-
};
|
|
980
|
-
|
|
981
|
-
const remove = () => {
|
|
982
|
-
// remove this point from points
|
|
983
|
-
const index = this.removePoint(point);
|
|
984
|
-
if (this.polygon?.closed()) {
|
|
985
|
-
// We should be checking if this.points is defined before rotating them.
|
|
986
|
-
// @ts-expect-error - TS4104 - The type 'readonly any[]' is 'readonly' and cannot be assigned to the mutable type 'any[]'. | TS2345 - Argument of type 'number | null | undefined' is not assignable to parameter of type 'number | undefined'.
|
|
987
|
-
this.points = rotate(this.points, index);
|
|
988
|
-
this.polygon?.update({closed: false});
|
|
989
|
-
}
|
|
990
|
-
this.updatePolygon();
|
|
991
|
-
// the polygon is now unclosed, so we need to
|
|
992
|
-
// remove any points props
|
|
993
|
-
this.clearCoords();
|
|
994
|
-
|
|
995
|
-
// remove this movablePoint from graphie.
|
|
996
|
-
// wait to do this until we're not inside of
|
|
997
|
-
// said point's onMoveEnd method so state is
|
|
998
|
-
// consistent throughout the method call
|
|
999
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
1000
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
1001
|
-
setTimeout(point.remove.bind(point), 0);
|
|
1002
|
-
};
|
|
1003
|
-
|
|
1004
|
-
const setInitialMove = debounce((point, bool) => {
|
|
1005
|
-
point.state.isInitialMove = bool;
|
|
1006
|
-
}, 1000);
|
|
1007
|
-
|
|
1008
|
-
const onMoveEndHandler = (coord) => {
|
|
1009
|
-
// If the user clicked and dragged a point over endpoint,
|
|
1010
|
-
// join the them
|
|
1011
|
-
const overlappedPoints =
|
|
1012
|
-
this.points.length > 1 &&
|
|
1013
|
-
((point === this.points[0] &&
|
|
1014
|
-
kpoint.equal(coord, _.last(this.points).coord())) ||
|
|
1015
|
-
(point === _.last(this.points) &&
|
|
1016
|
-
kpoint.equal(coord, this.points[0].coord())));
|
|
1017
|
-
|
|
1018
|
-
if (this.isClickToAddPoints()) {
|
|
1019
|
-
if (this.isCoordInTrash(coord)) {
|
|
1020
|
-
remove();
|
|
1021
|
-
} else if (overlappedPoints) {
|
|
1022
|
-
const pointToRemove = this.points.pop();
|
|
1023
|
-
|
|
1024
|
-
if (this.points.length > 2) {
|
|
1025
|
-
this.polygon?.update({closed: true});
|
|
1026
|
-
this.updateCoordsFromPoints();
|
|
1027
|
-
} else {
|
|
1028
|
-
this.polygon?.update({closed: false});
|
|
1029
|
-
this.clearCoords();
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
this.updatePolygon();
|
|
1033
|
-
// remove this movablePoint from graphie.
|
|
1034
|
-
// wait to do this until we're not inside of
|
|
1035
|
-
// said point's onMoveEnd method so state is
|
|
1036
|
-
// consistent throughout the method call
|
|
1037
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
1038
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
1039
|
-
setTimeout(pointToRemove.remove.bind(pointToRemove), 0);
|
|
1040
|
-
} else {
|
|
1041
|
-
// If the user clicked and dragged a point over any other
|
|
1042
|
-
// existing point, fix shape
|
|
1043
|
-
const shouldRemove = _.any(this.points, function (pt) {
|
|
1044
|
-
return pt !== point && kpoint.equal(pt.coord(), coord);
|
|
1045
|
-
});
|
|
1046
|
-
|
|
1047
|
-
if (shouldRemove) {
|
|
1048
|
-
this.removePoint(point);
|
|
1049
|
-
|
|
1050
|
-
if (this.points.length < 3) {
|
|
1051
|
-
this.polygon?.update({closed: false});
|
|
1052
|
-
this.clearCoords();
|
|
1053
|
-
} else if (this.polygon?.closed()) {
|
|
1054
|
-
this.updateCoordsFromPoints();
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
this.updatePolygon();
|
|
1058
|
-
// remove this movablePoint from graphie.
|
|
1059
|
-
// wait to do this until we're not inside
|
|
1060
|
-
// said point's onMoveEnd method so state
|
|
1061
|
-
// is consistent throughout the method call
|
|
1062
|
-
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
|
|
1063
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
1064
|
-
setTimeout(point.remove.bind(point), 0);
|
|
1065
|
-
} else {
|
|
1066
|
-
// If this was
|
|
1067
|
-
// * a click on the first or last point
|
|
1068
|
-
// * and not a deletion
|
|
1069
|
-
// * and not a drag,
|
|
1070
|
-
// * and not a creation of a new point
|
|
1071
|
-
// (see !point.state.isInitialMove, below),
|
|
1072
|
-
// * and our polygon is not closed,
|
|
1073
|
-
// * and we can close it (we need at least the same
|
|
1074
|
-
// number of points as coordinates asked for in the
|
|
1075
|
-
// question),
|
|
1076
|
-
// then close it
|
|
1077
|
-
const isFirstOrLastPoint =
|
|
1078
|
-
point === _.head(this.points) ||
|
|
1079
|
-
point === _.last(this.points);
|
|
1080
|
-
const pointNotMoved = !point.hasMoved();
|
|
1081
|
-
// NOTE (jsatk): Currently different on mobile versus
|
|
1082
|
-
// destkop
|
|
1083
|
-
const notANewPoint = !point.state.isInitialMove;
|
|
1084
|
-
const polygonNotClosed = !this.polygon?.closed();
|
|
1085
|
-
const allPointsPlotted = this.points.length > 2;
|
|
1086
|
-
|
|
1087
|
-
const canCloseUnclosedPolygon =
|
|
1088
|
-
isFirstOrLastPoint &&
|
|
1089
|
-
pointNotMoved &&
|
|
1090
|
-
notANewPoint &&
|
|
1091
|
-
polygonNotClosed &&
|
|
1092
|
-
allPointsPlotted;
|
|
1093
|
-
|
|
1094
|
-
if (canCloseUnclosedPolygon) {
|
|
1095
|
-
this.polygon?.update({closed: true});
|
|
1096
|
-
this.updatePolygon();
|
|
1097
|
-
|
|
1098
|
-
// We finally have a closed polygon, so save our
|
|
1099
|
-
// points to props
|
|
1100
|
-
this.updateCoordsFromPoints();
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
// In case we mouseup'd off the graphie and that
|
|
1106
|
-
// stopped the move
|
|
1107
|
-
this.setTrashCanVisibility(0.5);
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
setInitialMove(point, false);
|
|
1111
|
-
};
|
|
1112
|
-
|
|
1113
|
-
const graphConstraint = (coord: any) => {
|
|
1114
|
-
// These constraints are all relative to the other points, so if
|
|
1115
|
-
// we're creating the initial points and haven't added any others
|
|
1116
|
-
// to the graph, we can't enforce them.
|
|
1117
|
-
if (this.points == null || this.points.length === 0) {
|
|
1118
|
-
return true;
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
const coords: Array<Coord> = _.invoke(this.points, "coord");
|
|
1122
|
-
coords[i] = coord;
|
|
1123
|
-
|
|
1124
|
-
// Check for invalid positioning, but only if we aren't adding
|
|
1125
|
-
// points one click at a time, since those added points could
|
|
1126
|
-
// have already violated these constraints
|
|
1127
|
-
if (!this.isClickToAddPoints()) {
|
|
1128
|
-
// Polygons can't have consecutive collinear points
|
|
1129
|
-
if (
|
|
1130
|
-
collinear(coords[rel(-2)], coords[rel(-1)], coords[i]) ||
|
|
1131
|
-
collinear(coords[rel(-1)], coords[i], coords[rel(1)]) ||
|
|
1132
|
-
collinear(coords[i], coords[rel(1)], coords[rel(2)])
|
|
1133
|
-
) {
|
|
1134
|
-
return false;
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
const segments = _.zip(coords, rotate(coords));
|
|
1138
|
-
|
|
1139
|
-
if (this.points.length > 3) {
|
|
1140
|
-
// Constrain to simple (non self-intersecting) polygon by
|
|
1141
|
-
// testing whether adjacent segments intersect any others
|
|
1142
|
-
for (let j = -1; j <= 0; j++) {
|
|
1143
|
-
const segment = segments[rel(j)];
|
|
1144
|
-
const others = _.without(
|
|
1145
|
-
segments,
|
|
1146
|
-
segment,
|
|
1147
|
-
segments[rel(j - 1)],
|
|
1148
|
-
segments[rel(j + 1)],
|
|
1149
|
-
);
|
|
1150
|
-
|
|
1151
|
-
for (let k = 0; k < others.length; k++) {
|
|
1152
|
-
const other = others[k];
|
|
1153
|
-
// @ts-expect-error - TS2345 - Argument of type 'any[]' is not assignable to parameter of type 'Line'.
|
|
1154
|
-
if (intersects(segment, other)) {
|
|
1155
|
-
return false;
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
if (
|
|
1163
|
-
// @ts-expect-error - TS2339 - Property 'snapTo' does not exist on type 'PerseusGraphType'.
|
|
1164
|
-
this.props.graph.snapTo === "angles" &&
|
|
1165
|
-
this.points.length > 2
|
|
1166
|
-
) {
|
|
1167
|
-
// Snap to whole degree interior angles
|
|
1168
|
-
|
|
1169
|
-
const angles = _.map(angleMeasures(coords), function (rad) {
|
|
1170
|
-
return (rad * 180) / Math.PI;
|
|
1171
|
-
});
|
|
1172
|
-
|
|
1173
|
-
_.each([-1, 1], function (j) {
|
|
1174
|
-
angles[rel(j)] = Math.round(angles[rel(j)]);
|
|
1175
|
-
});
|
|
1176
|
-
|
|
1177
|
-
const getAngle = function (a: number, vertex, b: number) {
|
|
1178
|
-
const angle = GraphUtils.findAngle(
|
|
1179
|
-
coords[rel(a)],
|
|
1180
|
-
coords[rel(b)],
|
|
1181
|
-
coords[rel(vertex)],
|
|
1182
|
-
);
|
|
1183
|
-
return (angle + 360) % 360;
|
|
1184
|
-
};
|
|
1185
|
-
|
|
1186
|
-
const innerAngles = [
|
|
1187
|
-
angles[rel(-1)] - getAngle(-2, -1, 1),
|
|
1188
|
-
angles[rel(1)] - getAngle(-1, 1, 2),
|
|
1189
|
-
];
|
|
1190
|
-
innerAngles[2] = 180 - (innerAngles[0] + innerAngles[1]);
|
|
1191
|
-
|
|
1192
|
-
// Avoid degenerate triangles
|
|
1193
|
-
if (
|
|
1194
|
-
_.any(innerAngles, function (angle) {
|
|
1195
|
-
return leq(angle, 1);
|
|
1196
|
-
})
|
|
1197
|
-
) {
|
|
1198
|
-
return false;
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
const knownSide = magnitude(
|
|
1202
|
-
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
1203
|
-
vector(coords[rel(-1)], coords[rel(1)]),
|
|
1204
|
-
);
|
|
1205
|
-
|
|
1206
|
-
const onLeft =
|
|
1207
|
-
sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
|
|
1208
|
-
|
|
1209
|
-
// Solve for side by using the law of sines
|
|
1210
|
-
const side =
|
|
1211
|
-
(Math.sin((innerAngles[1] * Math.PI) / 180) /
|
|
1212
|
-
Math.sin((innerAngles[2] * Math.PI) / 180)) *
|
|
1213
|
-
knownSide;
|
|
1214
|
-
|
|
1215
|
-
const outerAngle = GraphUtils.findAngle(
|
|
1216
|
-
coords[rel(1)],
|
|
1217
|
-
coords[rel(-1)],
|
|
1218
|
-
);
|
|
1219
|
-
|
|
1220
|
-
const offset = polar(
|
|
1221
|
-
side,
|
|
1222
|
-
outerAngle + (onLeft ? 1 : -1) * innerAngles[0],
|
|
1223
|
-
);
|
|
1224
|
-
|
|
1225
|
-
return this.graphie?.addPoints(coords[rel(-1)], offset);
|
|
1226
|
-
}
|
|
1227
|
-
// @ts-expect-error - TS2339 - Property 'snapTo' does not exist on type 'PerseusGraphType'.
|
|
1228
|
-
if (this.props.graph.snapTo === "sides" && this.points.length > 1) {
|
|
1229
|
-
// Snap to whole unit side measures
|
|
1230
|
-
|
|
1231
|
-
const sides = _.map(
|
|
1232
|
-
[
|
|
1233
|
-
[coords[rel(-1)], coords[i]],
|
|
1234
|
-
[coords[i], coords[rel(1)]],
|
|
1235
|
-
[coords[rel(-1)], coords[rel(1)]],
|
|
1236
|
-
],
|
|
1237
|
-
function (coords) {
|
|
1238
|
-
// @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.
|
|
1239
|
-
return magnitude(vector(...coords));
|
|
1240
|
-
},
|
|
1241
|
-
);
|
|
1242
|
-
|
|
1243
|
-
_.each([0, 1], function (j) {
|
|
1244
|
-
sides[j] = Math.round(sides[j]);
|
|
1245
|
-
});
|
|
1246
|
-
|
|
1247
|
-
// Avoid degenerate triangles
|
|
1248
|
-
if (
|
|
1249
|
-
leq(sides[1] + sides[2], sides[0]) ||
|
|
1250
|
-
leq(sides[0] + sides[2], sides[1]) ||
|
|
1251
|
-
leq(sides[0] + sides[1], sides[2])
|
|
1252
|
-
) {
|
|
1253
|
-
return false;
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
// Solve for angle by using the law of cosines
|
|
1257
|
-
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
1258
|
-
|
|
1259
|
-
const outerAngle = GraphUtils.findAngle(
|
|
1260
|
-
coords[rel(1)],
|
|
1261
|
-
coords[rel(-1)],
|
|
1262
|
-
);
|
|
1263
|
-
|
|
1264
|
-
const onLeft =
|
|
1265
|
-
sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
|
|
1266
|
-
|
|
1267
|
-
const offset = polar(
|
|
1268
|
-
sides[0],
|
|
1269
|
-
outerAngle + (onLeft ? 1 : -1) * innerAngle,
|
|
1270
|
-
);
|
|
1271
|
-
|
|
1272
|
-
return this.graphie?.addPoints(coords[rel(-1)], offset);
|
|
1273
|
-
}
|
|
1274
|
-
// Snap to grid (already done)
|
|
1275
|
-
return true;
|
|
1276
|
-
};
|
|
1277
|
-
|
|
1278
|
-
const point = Interactive2.addMaybeMobileMovablePoint(this, {
|
|
1279
|
-
coord: coord,
|
|
1280
|
-
constraints: [
|
|
1281
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
1282
|
-
Interactive2.MovablePoint.constraints.bound(),
|
|
1283
|
-
snapToGrid
|
|
1284
|
-
? // @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
1285
|
-
Interactive2.MovablePoint.constraints.snap()
|
|
1286
|
-
: null,
|
|
1287
|
-
graphConstraint,
|
|
1288
|
-
],
|
|
1289
|
-
onMoveStart: () => {
|
|
1290
|
-
if (this.isClickToAddPoints()) {
|
|
1291
|
-
this.setTrashCanVisibility(1);
|
|
1292
|
-
}
|
|
1293
|
-
},
|
|
1294
|
-
onMove: () => {
|
|
1295
|
-
if (this.polygon?.closed()) {
|
|
1296
|
-
this.updateCoordsFromPoints();
|
|
1297
|
-
}
|
|
1298
|
-
},
|
|
1299
|
-
onMoveEnd: onMoveEndHandler,
|
|
1300
|
-
...(this.props.apiOptions.isMobile && this.isClickToAddPoints()
|
|
1301
|
-
? {onRemove: remove}
|
|
1302
|
-
: {}),
|
|
1303
|
-
});
|
|
1304
|
-
|
|
1305
|
-
point.state.isInitialMove = true;
|
|
1306
|
-
|
|
1307
|
-
return point;
|
|
1308
|
-
};
|
|
1309
|
-
|
|
1310
|
-
updateCoordsFromPoints: () => void = () => {
|
|
1311
|
-
const graph = _.extend({}, this.props.graph, {
|
|
1312
|
-
// Handle old movable points with .coord, or
|
|
1313
|
-
// Interactive2.MovablePoint's with .coord()
|
|
1314
|
-
coords: _.map(this.points, function (point) {
|
|
1315
|
-
return _.result(point, "coord");
|
|
1316
|
-
}),
|
|
1317
|
-
});
|
|
1318
|
-
this.onChange({graph: graph});
|
|
1319
|
-
};
|
|
1320
|
-
|
|
1321
|
-
clearCoords: () => void = () => {
|
|
1322
|
-
const graph = _.extend({}, this.props.graph, {
|
|
1323
|
-
coords: null,
|
|
1324
|
-
});
|
|
1325
|
-
this.onChange({graph: graph});
|
|
1326
|
-
};
|
|
1327
|
-
|
|
1328
|
-
onChange: (arg1: any) => void = (data) => {
|
|
1329
|
-
this.props.onChange(data);
|
|
1330
|
-
this.props.trackInteraction();
|
|
1331
|
-
};
|
|
1332
|
-
|
|
1333
|
-
addPointControls: () => void = () => {
|
|
1334
|
-
if (this.props.graph.type !== "point") {
|
|
1335
|
-
throw makeInvalidTypeError("addPointControls", "point");
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
const coords = InteractiveGraph.getPointCoords(
|
|
1339
|
-
this.props.graph,
|
|
1340
|
-
this.props,
|
|
1341
|
-
);
|
|
1342
|
-
// Clear out our old points so that newly added points don't
|
|
1343
|
-
// "collide" with them and reposition when being added
|
|
1344
|
-
// Without this, when added, each point checks whether it is on top
|
|
1345
|
-
// of a point in this.points, which (a) shouldn't matter since
|
|
1346
|
-
// we're clearing out this.points anyways, and (b) can cause problems
|
|
1347
|
-
// if each of this.points is a MovablePoint instead of an
|
|
1348
|
-
// Interactive2.MovablePoint, since one has a .coord and the other
|
|
1349
|
-
// has .coord()
|
|
1350
|
-
// TODO(jack): Figure out a better way to do this
|
|
1351
|
-
this.points = [];
|
|
1352
|
-
this.points = _.map(coords, this.createPointForPointsType, this);
|
|
1353
|
-
};
|
|
1354
|
-
|
|
1355
|
-
removePointControls: () => void = () => {
|
|
1356
|
-
_.invoke(this.points, "remove");
|
|
1357
|
-
};
|
|
1358
|
-
|
|
1359
|
-
addSegmentControls: () => void = () => {
|
|
1360
|
-
if (this.props.graph.type !== "segment") {
|
|
1361
|
-
throw makeInvalidTypeError("addSegmentControls", "segment");
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
const self = this;
|
|
1365
|
-
const graphie = this.graphie;
|
|
1366
|
-
|
|
1367
|
-
const coords = InteractiveGraph.getSegmentCoords(
|
|
1368
|
-
this.props.graph,
|
|
1369
|
-
this.props,
|
|
1370
|
-
);
|
|
1371
|
-
|
|
1372
|
-
const createPoint = (options: {
|
|
1373
|
-
constraints: Array<any | ((coord: never) => boolean)>;
|
|
1374
|
-
coord: any;
|
|
1375
|
-
onMove: () => void;
|
|
1376
|
-
}) => Interactive2.addMaybeMobileMovablePoint(this, options);
|
|
1377
|
-
|
|
1378
|
-
this.points = [];
|
|
1379
|
-
this.lines = _.map(
|
|
1380
|
-
coords,
|
|
1381
|
-
function (segment, i) {
|
|
1382
|
-
const updateCoordProps = function () {
|
|
1383
|
-
const graph = _.extend({}, self.props.graph, {
|
|
1384
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
1385
|
-
coords: _.invoke(self.lines, "coords"),
|
|
1386
|
-
});
|
|
1387
|
-
self.onChange({graph: graph});
|
|
1388
|
-
};
|
|
1389
|
-
|
|
1390
|
-
// NOTE(jeresig): This code attempts to access the variable
|
|
1391
|
-
// while it's still being defined, it should be refactored!
|
|
1392
|
-
// The callbacks appear to be executed synchronously, which
|
|
1393
|
-
// causes this issue.
|
|
1394
|
-
const points: Array<any> = [];
|
|
1395
|
-
|
|
1396
|
-
for (let i = 0; i < segment.length; i += 1) {
|
|
1397
|
-
const coord = segment[i];
|
|
1398
|
-
points.push(
|
|
1399
|
-
createPoint({
|
|
1400
|
-
coord: coord,
|
|
1401
|
-
constraints: [
|
|
1402
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
1403
|
-
Interactive2.MovablePoint.constraints.bound(),
|
|
1404
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
1405
|
-
Interactive2.MovablePoint.constraints.snap(),
|
|
1406
|
-
(coord: any) => {
|
|
1407
|
-
const otherPoint = points[1 - i];
|
|
1408
|
-
if (!otherPoint) {
|
|
1409
|
-
// point hasn't been defined yet
|
|
1410
|
-
// because we're still creating them
|
|
1411
|
-
// We return false as the points aren't
|
|
1412
|
-
// going to be in the same position at
|
|
1413
|
-
// this point.
|
|
1414
|
-
return false;
|
|
1415
|
-
}
|
|
1416
|
-
return !kpoint.equal(
|
|
1417
|
-
coord,
|
|
1418
|
-
otherPoint.coord(),
|
|
1419
|
-
);
|
|
1420
|
-
},
|
|
1421
|
-
],
|
|
1422
|
-
onMove: updateCoordProps,
|
|
1423
|
-
}),
|
|
1424
|
-
);
|
|
1425
|
-
}
|
|
1426
|
-
|
|
1427
|
-
self.points = self.points.concat(points);
|
|
1428
|
-
const line = Interactive2.addMovableLine(graphie, {
|
|
1429
|
-
points: points,
|
|
1430
|
-
static: false,
|
|
1431
|
-
constraints: [
|
|
1432
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
1433
|
-
Interactive2.MovableLine.constraints.bound(),
|
|
1434
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
1435
|
-
Interactive2.MovableLine.constraints.snap(),
|
|
1436
|
-
],
|
|
1437
|
-
onMove: [
|
|
1438
|
-
// @ts-expect-error - TS2339 - Property 'onMove' does not exist on type '(graphie: any, movable: any, options: any) => void'.
|
|
1439
|
-
Interactive2.MovableLine.onMove.updatePoints,
|
|
1440
|
-
updateCoordProps,
|
|
1441
|
-
],
|
|
1442
|
-
normalStyle: {
|
|
1443
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
1444
|
-
stroke: this.props.apiOptions.isMobile
|
|
1445
|
-
? KhanColors.BLUE_C
|
|
1446
|
-
: KhanColors.INTERACTIVE,
|
|
1447
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
1448
|
-
...this._lineStroke(),
|
|
1449
|
-
},
|
|
1450
|
-
highlightStyle: {
|
|
1451
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
1452
|
-
stroke: this.props.apiOptions.isMobile
|
|
1453
|
-
? KhanColors.BLUE_C
|
|
1454
|
-
: KhanColors.INTERACTING,
|
|
1455
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
1456
|
-
...this._lineStroke(),
|
|
1457
|
-
},
|
|
1458
|
-
});
|
|
1459
|
-
_.invoke(points, "toFront");
|
|
1460
|
-
|
|
1461
|
-
return line;
|
|
1462
|
-
},
|
|
1463
|
-
this,
|
|
1464
|
-
);
|
|
1465
|
-
};
|
|
1466
|
-
|
|
1467
|
-
removeSegmentControls: () => void = () => {
|
|
1468
|
-
_.invoke(this.points, "remove");
|
|
1469
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly (readonly Coord[])[] | null | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
1470
|
-
_.invoke(this.lines, "remove");
|
|
1471
|
-
};
|
|
1472
|
-
|
|
1473
|
-
addRayControls: () => void = () => {
|
|
1474
|
-
this.addLine("ray");
|
|
1475
|
-
};
|
|
1476
|
-
|
|
1477
|
-
removeRayControls: () => void = () => {
|
|
1478
|
-
this.removeLine();
|
|
1479
|
-
};
|
|
1480
|
-
|
|
1481
|
-
addPolygonControls: () => void = () => {
|
|
1482
|
-
this.polygon = null;
|
|
1483
|
-
const coords = InteractiveGraph.getPolygonCoords(
|
|
1484
|
-
this.props.graph,
|
|
1485
|
-
this.props,
|
|
1486
|
-
);
|
|
1487
|
-
// Clear out our old points so that newly added points don't
|
|
1488
|
-
// "collide", as in `addPointControls`
|
|
1489
|
-
this.points = [];
|
|
1490
|
-
this.points = _.map(coords, this.createPointForPolygonType, this);
|
|
1491
|
-
this.updatePolygon();
|
|
1492
|
-
};
|
|
1493
|
-
|
|
1494
|
-
updatePolygon: () => void = () => {
|
|
1495
|
-
if (this.props.graph.type !== "polygon") {
|
|
1496
|
-
throw makeInvalidTypeError("updatePolygon", "polygon");
|
|
1497
|
-
}
|
|
1498
|
-
|
|
1499
|
-
let closed;
|
|
1500
|
-
if (this.polygon) {
|
|
1501
|
-
closed = this.polygon.closed();
|
|
1502
|
-
} else if (this.points.length >= 3) {
|
|
1503
|
-
closed = true;
|
|
1504
|
-
} else {
|
|
1505
|
-
// There will only be fewer than 3 points in click-to-add-vertices
|
|
1506
|
-
// mode, so we don't need to explicitly check for that here.
|
|
1507
|
-
closed = false;
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
const graphie = this.graphie;
|
|
1511
|
-
const n = this.points.length;
|
|
1512
|
-
|
|
1513
|
-
// TODO(alex): check against "grid" instead, use constants
|
|
1514
|
-
const snapToGrid = !_.contains(
|
|
1515
|
-
["angles", "sides"],
|
|
1516
|
-
this.props.graph.snapTo,
|
|
1517
|
-
);
|
|
1518
|
-
|
|
1519
|
-
const angleLabels = _.times(
|
|
1520
|
-
n,
|
|
1521
|
-
function (i) {
|
|
1522
|
-
if (
|
|
1523
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
1524
|
-
!this.props.graph.showAngles ||
|
|
1525
|
-
(!closed && (i === 0 || i === n - 1))
|
|
1526
|
-
) {
|
|
1527
|
-
return "";
|
|
1528
|
-
}
|
|
1529
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
1530
|
-
if (this.props.graph.snapTo === "angles") {
|
|
1531
|
-
return "$deg0";
|
|
1532
|
-
}
|
|
1533
|
-
return "$deg1";
|
|
1534
|
-
},
|
|
1535
|
-
this,
|
|
1536
|
-
);
|
|
1537
|
-
|
|
1538
|
-
const showRightAngleMarkers = _.times(
|
|
1539
|
-
n,
|
|
1540
|
-
function (i) {
|
|
1541
|
-
return closed || (i !== 0 && i !== n - 1);
|
|
1542
|
-
},
|
|
1543
|
-
this,
|
|
1544
|
-
);
|
|
1545
|
-
|
|
1546
|
-
const numArcs = _.times(
|
|
1547
|
-
n,
|
|
1548
|
-
function (i) {
|
|
1549
|
-
if (
|
|
1550
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
1551
|
-
this.props.graph.showAngles &&
|
|
1552
|
-
(closed || (i !== 0 && i !== n - 1))
|
|
1553
|
-
) {
|
|
1554
|
-
return 1;
|
|
1555
|
-
}
|
|
1556
|
-
return 0;
|
|
1557
|
-
},
|
|
1558
|
-
this,
|
|
1559
|
-
);
|
|
1560
|
-
|
|
1561
|
-
const sideLabels = _.times(
|
|
1562
|
-
n,
|
|
1563
|
-
function (i) {
|
|
1564
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
1565
|
-
if (!this.props.graph.showSides || (!closed && i === n - 1)) {
|
|
1566
|
-
return "";
|
|
1567
|
-
}
|
|
1568
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
1569
|
-
if (this.props.graph.snapTo === "sides") {
|
|
1570
|
-
return "$len0";
|
|
1571
|
-
}
|
|
1572
|
-
return "$len1";
|
|
1573
|
-
},
|
|
1574
|
-
this,
|
|
1575
|
-
);
|
|
1576
|
-
|
|
1577
|
-
if (this.polygon == null) {
|
|
1578
|
-
const self = this;
|
|
1579
|
-
self.polygon = Interactive2.addMovablePolygon(graphie, {
|
|
1580
|
-
constraints: [
|
|
1581
|
-
// @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
|
|
1582
|
-
Interactive2.MovablePolygon.constraints.bound(),
|
|
1583
|
-
snapToGrid
|
|
1584
|
-
? // @ts-expect-error - TS2339 - Property 'constraints' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
|
|
1585
|
-
Interactive2.MovablePolygon.constraints.snap()
|
|
1586
|
-
: null,
|
|
1587
|
-
],
|
|
1588
|
-
closed: closed,
|
|
1589
|
-
points: self.points,
|
|
1590
|
-
angleLabels: angleLabels,
|
|
1591
|
-
showRightAngleMarkers: showRightAngleMarkers,
|
|
1592
|
-
numArcs: numArcs,
|
|
1593
|
-
sideLabels: sideLabels,
|
|
1594
|
-
onMove: [
|
|
1595
|
-
// @ts-expect-error - TS2339 - Property 'onMove' does not exist on type '(graphie: any, movable: any, options: any) => undefined'.
|
|
1596
|
-
Interactive2.MovablePolygon.onMove.updatePoints,
|
|
1597
|
-
function () {
|
|
1598
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
1599
|
-
if (this.closed()) {
|
|
1600
|
-
self.updateCoordsFromPoints();
|
|
1601
|
-
}
|
|
1602
|
-
},
|
|
1603
|
-
],
|
|
1604
|
-
normalStyle: {
|
|
1605
|
-
stroke: this.props.apiOptions.isMobile
|
|
1606
|
-
? KhanColors.BLUE_C
|
|
1607
|
-
: KhanColors.INTERACTIVE,
|
|
1608
|
-
...this._lineStroke(),
|
|
1609
|
-
},
|
|
1610
|
-
});
|
|
1611
|
-
} else {
|
|
1612
|
-
// We only need to pass in the properties that might've changed
|
|
1613
|
-
this.polygon.update({
|
|
1614
|
-
closed: closed,
|
|
1615
|
-
points: this.points,
|
|
1616
|
-
angleLabels: angleLabels,
|
|
1617
|
-
showRightAngleMarkers: showRightAngleMarkers,
|
|
1618
|
-
numArcs: numArcs,
|
|
1619
|
-
sideLabels: sideLabels,
|
|
1620
|
-
});
|
|
1621
|
-
}
|
|
1622
|
-
};
|
|
1623
|
-
|
|
1624
|
-
removePolygonControls: () => void = () => {
|
|
1625
|
-
_.invoke(this.points, "remove");
|
|
1626
|
-
this.polygon?.remove();
|
|
1627
|
-
};
|
|
1628
|
-
|
|
1629
|
-
addAngleControls: () => void = () => {
|
|
1630
|
-
if (this.props.graph.type !== "angle") {
|
|
1631
|
-
throw makeInvalidTypeError("addAngleControls", "angle");
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
const graph = this.props.graph;
|
|
1635
|
-
const graphie = this.graphie;
|
|
1636
|
-
|
|
1637
|
-
const coords = InteractiveGraph.getAngleCoords(
|
|
1638
|
-
this.props.graph,
|
|
1639
|
-
this.props,
|
|
1640
|
-
);
|
|
1641
|
-
|
|
1642
|
-
// The vertex snaps to the grid, but the rays don't...
|
|
1643
|
-
this.points = coords.map(function (coord, i) {
|
|
1644
|
-
return graphie?.addMovablePoint(
|
|
1645
|
-
_.extend(
|
|
1646
|
-
{
|
|
1647
|
-
coord: coord,
|
|
1648
|
-
normalStyle: {
|
|
1649
|
-
stroke: KhanColors.INTERACTIVE,
|
|
1650
|
-
fill: KhanColors.INTERACTIVE,
|
|
1651
|
-
},
|
|
1652
|
-
},
|
|
1653
|
-
i === 1
|
|
1654
|
-
? {
|
|
1655
|
-
snapX: graphie.snap[0],
|
|
1656
|
-
snapY: graphie.snap[1],
|
|
1657
|
-
}
|
|
1658
|
-
: {},
|
|
1659
|
-
),
|
|
1660
|
-
);
|
|
1661
|
-
});
|
|
1662
|
-
|
|
1663
|
-
// ...they snap to whole-degree angles from the vertex.
|
|
1664
|
-
this.angle = graphie?.addMovableAngle({
|
|
1665
|
-
points: this.points,
|
|
1666
|
-
snapDegrees: graph.snapDegrees || 1,
|
|
1667
|
-
snapOffsetDeg: graph.angleOffsetDeg || 0,
|
|
1668
|
-
angleLabel: graph.showAngles ? "$deg0" : "",
|
|
1669
|
-
pushOut: 2,
|
|
1670
|
-
allowReflex: defaultVal(graph.allowReflexAngles, true),
|
|
1671
|
-
});
|
|
1672
|
-
|
|
1673
|
-
$(this.angle).on("move", () => {
|
|
1674
|
-
this.onChange({
|
|
1675
|
-
graph: {...graph, coords: this.angle?.getClockwiseCoords()},
|
|
1676
|
-
});
|
|
1677
|
-
});
|
|
1678
|
-
};
|
|
1679
|
-
|
|
1680
|
-
removeAngleControls: () => void = () => {
|
|
1681
|
-
_.invoke(this.points, "remove");
|
|
1682
|
-
this.angle?.remove();
|
|
1683
|
-
};
|
|
1684
|
-
|
|
1685
|
-
toggleShowSides: () => void = () => {
|
|
1686
|
-
if (this.props.graph.type !== "polygon") {
|
|
1687
|
-
throw makeInvalidTypeError("toggleShowSides", "polygon");
|
|
1688
|
-
}
|
|
1689
|
-
const graph = _.extend({}, this.props.graph, {
|
|
1690
|
-
showSides: !this.props.graph.showSides,
|
|
1691
|
-
});
|
|
1692
|
-
this.onChange({graph: graph});
|
|
1693
|
-
};
|
|
1694
|
-
|
|
1695
|
-
getUserInput: () => PerseusGraphType = () => {
|
|
1696
|
-
return InteractiveGraph.getUserInputFromProps(this.props);
|
|
1697
|
-
};
|
|
1698
|
-
|
|
1699
|
-
simpleValidate: (rubric: Rubric) => PerseusScore = (rubric) => {
|
|
1700
|
-
return InteractiveGraph.validate(this.getUserInput(), rubric, this);
|
|
1701
|
-
};
|
|
1702
|
-
|
|
1703
|
-
focus: () => void = $.noop;
|
|
1704
|
-
|
|
1705
|
-
render(): React.ReactNode {
|
|
1706
|
-
const box = getInteractiveBoxFromSizeClass(
|
|
1707
|
-
this.props.containerSizeClass,
|
|
1708
|
-
);
|
|
1709
|
-
|
|
1710
|
-
let instructions;
|
|
1711
|
-
if (this.isClickToAddPoints() && this.state.shouldShowInstructions) {
|
|
1712
|
-
if (this.props.graph.type === "point") {
|
|
1713
|
-
instructions = i18n._("Click to add points");
|
|
1714
|
-
} else if (this.props.graph.type === "polygon") {
|
|
1715
|
-
instructions = i18n._("Click to add vertices");
|
|
1716
|
-
}
|
|
1717
|
-
} else {
|
|
1718
|
-
instructions = undefined;
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
|
-
let onMouseDown;
|
|
1722
|
-
if (this.isClickToAddPoints()) {
|
|
1723
|
-
onMouseDown = this.handleAddPointsMouseDown;
|
|
1724
|
-
}
|
|
1725
|
-
|
|
1726
|
-
const gridStep =
|
|
1727
|
-
this.props.gridStep ||
|
|
1728
|
-
Util.getGridStep(this.props.range, this.props.step, box[0]);
|
|
1729
|
-
const snapStep =
|
|
1730
|
-
this.props.snapStep || Util.snapStepFromGridStep(gridStep);
|
|
1731
|
-
|
|
1732
|
-
const isMobile = this.props.apiOptions.isMobile;
|
|
1733
|
-
|
|
1734
|
-
return (
|
|
1735
|
-
<div
|
|
1736
|
-
className={
|
|
1737
|
-
"perseus-widget " + "perseus-widget-interactive-graph"
|
|
1738
|
-
}
|
|
1739
|
-
style={{
|
|
1740
|
-
width: box[0],
|
|
1741
|
-
height: box[1],
|
|
1742
|
-
}}
|
|
1743
|
-
>
|
|
1744
|
-
<Graph
|
|
1745
|
-
instructions={instructions}
|
|
1746
|
-
// eslint-disable-next-line react/no-string-refs
|
|
1747
|
-
ref="graph"
|
|
1748
|
-
box={box}
|
|
1749
|
-
labels={this.props.labels}
|
|
1750
|
-
range={this.props.range}
|
|
1751
|
-
step={
|
|
1752
|
-
isMobile
|
|
1753
|
-
? Util.constrainedTickStepsFromTickSteps(
|
|
1754
|
-
this.props.step,
|
|
1755
|
-
this.props.range,
|
|
1756
|
-
)
|
|
1757
|
-
: this.props.step
|
|
1758
|
-
}
|
|
1759
|
-
gridStep={gridStep}
|
|
1760
|
-
snapStep={snapStep}
|
|
1761
|
-
markings={this.props.markings}
|
|
1762
|
-
backgroundImage={this.props.backgroundImage}
|
|
1763
|
-
showProtractor={this.props.showProtractor}
|
|
1764
|
-
showRuler={this.props.showRuler}
|
|
1765
|
-
rulerLabel={this.props.rulerLabel}
|
|
1766
|
-
rulerTicks={this.props.rulerTicks}
|
|
1767
|
-
onMouseDown={onMouseDown}
|
|
1768
|
-
onGraphieUpdated={this.setGraphie}
|
|
1769
|
-
setDrawingAreaAvailable={
|
|
1770
|
-
this.props.apiOptions.setDrawingAreaAvailable
|
|
1771
|
-
}
|
|
1772
|
-
isMobile={isMobile}
|
|
1773
|
-
/>
|
|
1774
|
-
</div>
|
|
1775
|
-
);
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
|
-
static getQuadraticCoefficients(
|
|
1779
|
-
coords: ReadonlyArray<Coord>,
|
|
1780
|
-
): QuadraticCoefficient {
|
|
1781
|
-
const p1 = coords[0];
|
|
1782
|
-
const p2 = coords[1];
|
|
1783
|
-
const p3 = coords[2];
|
|
1784
|
-
|
|
1785
|
-
const denom = (p1[0] - p2[0]) * (p1[0] - p3[0]) * (p2[0] - p3[0]);
|
|
1786
|
-
if (denom === 0) {
|
|
1787
|
-
// Many of the callers assume that the return value is always defined.
|
|
1788
|
-
// @ts-expect-error - TS2322 - Type 'undefined' is not assignable to type 'QuadraticCoefficient'.
|
|
1789
|
-
return;
|
|
1790
|
-
}
|
|
1791
|
-
const a =
|
|
1792
|
-
(p3[0] * (p2[1] - p1[1]) +
|
|
1793
|
-
p2[0] * (p1[1] - p3[1]) +
|
|
1794
|
-
p1[0] * (p3[1] - p2[1])) /
|
|
1795
|
-
denom;
|
|
1796
|
-
const b =
|
|
1797
|
-
(p3[0] * p3[0] * (p1[1] - p2[1]) +
|
|
1798
|
-
p2[0] * p2[0] * (p3[1] - p1[1]) +
|
|
1799
|
-
p1[0] * p1[0] * (p2[1] - p3[1])) /
|
|
1800
|
-
denom;
|
|
1801
|
-
const c =
|
|
1802
|
-
(p2[0] * p3[0] * (p2[0] - p3[0]) * p1[1] +
|
|
1803
|
-
p3[0] * p1[0] * (p3[0] - p1[0]) * p2[1] +
|
|
1804
|
-
p1[0] * p2[0] * (p1[0] - p2[0]) * p3[1]) /
|
|
1805
|
-
denom;
|
|
1806
|
-
return [a, b, c];
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
|
-
static getSinusoidCoefficients(
|
|
1810
|
-
coords: ReadonlyArray<Coord>,
|
|
1811
|
-
): SineCoefficient {
|
|
1812
|
-
// It's assumed that p1 is the root and p2 is the first peak
|
|
1813
|
-
const p1 = coords[0];
|
|
1814
|
-
const p2 = coords[1];
|
|
1815
|
-
|
|
1816
|
-
// Resulting coefficients are canonical for this sine curve
|
|
1817
|
-
const amplitude = p2[1] - p1[1];
|
|
1818
|
-
const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
|
|
1819
|
-
const phase = p1[0] * angularFrequency;
|
|
1820
|
-
const verticalOffset = p1[1];
|
|
1821
|
-
|
|
1822
|
-
return [amplitude, angularFrequency, phase, verticalOffset];
|
|
1823
|
-
}
|
|
1824
|
-
|
|
1825
|
-
/**
|
|
1826
|
-
* @param {object} graph Like props.graph or props.correct
|
|
1827
|
-
* @param {object} props of an InteractiveGraph instance
|
|
1828
|
-
*/
|
|
1829
|
-
static getLineCoords(
|
|
1830
|
-
graph: PerseusGraphType,
|
|
1831
|
-
props: Props,
|
|
1832
|
-
): ReadonlyArray<Coord> {
|
|
1833
|
-
return (
|
|
1834
|
-
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
1835
|
-
graph.coords ||
|
|
1836
|
-
InteractiveGraph.pointsFromNormalized(props, [
|
|
1837
|
-
[0.25, 0.75],
|
|
1838
|
-
[0.75, 0.75],
|
|
1839
|
-
])
|
|
1840
|
-
);
|
|
1841
|
-
}
|
|
1842
|
-
|
|
1843
|
-
/**
|
|
1844
|
-
* @param {object} graph Like props.graph or props.correct
|
|
1845
|
-
* @param {object} props of an InteractiveGraph instance
|
|
1846
|
-
*/
|
|
1847
|
-
static getPointCoords(
|
|
1848
|
-
graph: PerseusGraphTypePoint,
|
|
1849
|
-
props: Props,
|
|
1850
|
-
): ReadonlyArray<Coord> {
|
|
1851
|
-
const numPoints = graph.numPoints || 1;
|
|
1852
|
-
let coords = graph.coords;
|
|
1853
|
-
|
|
1854
|
-
if (coords) {
|
|
1855
|
-
return coords;
|
|
1856
|
-
}
|
|
1857
|
-
switch (numPoints) {
|
|
1858
|
-
case 1:
|
|
1859
|
-
// Back in the day, one point's coords were in graph.coord
|
|
1860
|
-
coords = [graph.coord || [0, 0]];
|
|
1861
|
-
break;
|
|
1862
|
-
case 2:
|
|
1863
|
-
coords = [
|
|
1864
|
-
[-5, 0],
|
|
1865
|
-
[5, 0],
|
|
1866
|
-
];
|
|
1867
|
-
break;
|
|
1868
|
-
case 3:
|
|
1869
|
-
coords = [
|
|
1870
|
-
[-5, 0],
|
|
1871
|
-
[0, 0],
|
|
1872
|
-
[5, 0],
|
|
1873
|
-
];
|
|
1874
|
-
break;
|
|
1875
|
-
case 4:
|
|
1876
|
-
coords = [
|
|
1877
|
-
[-6, 0],
|
|
1878
|
-
[-2, 0],
|
|
1879
|
-
[2, 0],
|
|
1880
|
-
[6, 0],
|
|
1881
|
-
];
|
|
1882
|
-
break;
|
|
1883
|
-
case 5:
|
|
1884
|
-
coords = [
|
|
1885
|
-
[-6, 0],
|
|
1886
|
-
[-3, 0],
|
|
1887
|
-
[0, 0],
|
|
1888
|
-
[3, 0],
|
|
1889
|
-
[6, 0],
|
|
1890
|
-
];
|
|
1891
|
-
break;
|
|
1892
|
-
case 6:
|
|
1893
|
-
coords = [
|
|
1894
|
-
[-5, 0],
|
|
1895
|
-
[-3, 0],
|
|
1896
|
-
[-1, 0],
|
|
1897
|
-
[1, 0],
|
|
1898
|
-
[3, 0],
|
|
1899
|
-
[5, 0],
|
|
1900
|
-
];
|
|
1901
|
-
break;
|
|
1902
|
-
case UNLIMITED:
|
|
1903
|
-
coords = [];
|
|
1904
|
-
break;
|
|
1905
|
-
}
|
|
1906
|
-
// Transform coords from their -10 to 10 space to 0 to 1
|
|
1907
|
-
// because of the old graph.coord, and also it's easier.
|
|
1908
|
-
const range = [
|
|
1909
|
-
[-10, 10],
|
|
1910
|
-
[-10, 10],
|
|
1911
|
-
];
|
|
1912
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
1913
|
-
const newCoords = InteractiveGraph.normalizeCoords(coords, range);
|
|
1914
|
-
|
|
1915
|
-
return InteractiveGraph.pointsFromNormalized(props, newCoords);
|
|
1916
|
-
}
|
|
1917
|
-
|
|
1918
|
-
/**
|
|
1919
|
-
* @param {object} graph Like props.graph or props.correct
|
|
1920
|
-
* @param {object} props of an InteractiveGraph instance
|
|
1921
|
-
*/
|
|
1922
|
-
static getLinearSystemCoords(
|
|
1923
|
-
graph: PerseusGraphType,
|
|
1924
|
-
props: Props,
|
|
1925
|
-
): ReadonlyArray<ReadonlyArray<Coord>> {
|
|
1926
|
-
return (
|
|
1927
|
-
// The callers assume that we're return an array of points
|
|
1928
|
-
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
1929
|
-
graph.coords ||
|
|
1930
|
-
_.map(
|
|
1931
|
-
[
|
|
1932
|
-
[
|
|
1933
|
-
[0.25, 0.75],
|
|
1934
|
-
[0.75, 0.75],
|
|
1935
|
-
],
|
|
1936
|
-
[
|
|
1937
|
-
[0.25, 0.25],
|
|
1938
|
-
[0.75, 0.25],
|
|
1939
|
-
],
|
|
1940
|
-
],
|
|
1941
|
-
(coords) => {
|
|
1942
|
-
// @ts-expect-error - TS2345 - Argument of type 'number[][]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
1943
|
-
return InteractiveGraph.pointsFromNormalized(props, coords);
|
|
1944
|
-
},
|
|
1945
|
-
)
|
|
1946
|
-
);
|
|
1947
|
-
}
|
|
1948
|
-
|
|
1949
|
-
/**
|
|
1950
|
-
* @param {object} graph Like props.graph or props.correct
|
|
1951
|
-
* @param {object} props of an InteractiveGraph instance
|
|
1952
|
-
*/
|
|
1953
|
-
static getPolygonCoords(
|
|
1954
|
-
graph: PerseusGraphType,
|
|
1955
|
-
props: Props,
|
|
1956
|
-
): ReadonlyArray<Coord> {
|
|
1957
|
-
if (graph.type !== "polygon") {
|
|
1958
|
-
throw makeInvalidTypeError("toggleShowSides", "polygon");
|
|
1959
|
-
}
|
|
1960
|
-
|
|
1961
|
-
let coords = graph.coords;
|
|
1962
|
-
if (coords) {
|
|
1963
|
-
return coords;
|
|
1964
|
-
}
|
|
1965
|
-
|
|
1966
|
-
const n = graph.numSides || 3;
|
|
1967
|
-
|
|
1968
|
-
if (n === UNLIMITED) {
|
|
1969
|
-
coords = [];
|
|
1970
|
-
} else {
|
|
1971
|
-
const angle = (2 * Math.PI) / n;
|
|
1972
|
-
const offset = (1 / n - 1 / 2) * Math.PI;
|
|
1973
|
-
|
|
1974
|
-
// TODO(alex): Generalize this to more than just triangles so that
|
|
1975
|
-
// all polygons have whole number side lengths if snapping to sides
|
|
1976
|
-
const radius =
|
|
1977
|
-
graph.snapTo === "sides" ? (Math.sqrt(3) / 3) * 7 : 4;
|
|
1978
|
-
|
|
1979
|
-
// Generate coords of a regular polygon with n sides
|
|
1980
|
-
coords = _.times(n, function (i) {
|
|
1981
|
-
return [
|
|
1982
|
-
radius * Math.cos(i * angle + offset),
|
|
1983
|
-
radius * Math.sin(i * angle + offset),
|
|
1984
|
-
];
|
|
1985
|
-
});
|
|
1986
|
-
}
|
|
1987
|
-
|
|
1988
|
-
const ranges: [Range, Range] = [
|
|
1989
|
-
[-10, 10],
|
|
1990
|
-
[-10, 10],
|
|
1991
|
-
];
|
|
1992
|
-
coords = InteractiveGraph.normalizeCoords(coords, ranges);
|
|
1993
|
-
|
|
1994
|
-
const snapToGrid = !_.contains(["angles", "sides"], graph.snapTo);
|
|
1995
|
-
coords = InteractiveGraph.pointsFromNormalized(
|
|
1996
|
-
props,
|
|
1997
|
-
coords,
|
|
1998
|
-
/* noSnap */ !snapToGrid,
|
|
1999
|
-
);
|
|
2000
|
-
|
|
2001
|
-
return coords;
|
|
2002
|
-
}
|
|
2003
|
-
|
|
2004
|
-
/**
|
|
2005
|
-
* @param {object} graph Like props.graph or props.correct
|
|
2006
|
-
* @param {object} props of an InteractiveGraph instance
|
|
2007
|
-
*/
|
|
2008
|
-
static getSegmentCoords(
|
|
2009
|
-
graph: PerseusGraphTypeSegment,
|
|
2010
|
-
props: Props,
|
|
2011
|
-
): ReadonlyArray<ReadonlyArray<Coord>> {
|
|
2012
|
-
const coords = graph.coords;
|
|
2013
|
-
if (coords) {
|
|
2014
|
-
return coords;
|
|
2015
|
-
}
|
|
2016
|
-
|
|
2017
|
-
const n = graph.numSegments || 1;
|
|
2018
|
-
// @ts-expect-error - TS2322 - Type 'number[] | undefined' is not assignable to type 'readonly number[]'.
|
|
2019
|
-
const ys: ReadonlyArray<number> = {
|
|
2020
|
-
1: [5],
|
|
2021
|
-
2: [5, -5],
|
|
2022
|
-
3: [5, 0, -5],
|
|
2023
|
-
4: [6, 2, -2, -6],
|
|
2024
|
-
5: [6, 3, 0, -3, -6],
|
|
2025
|
-
6: [5, 3, 1, -1, -3, -5],
|
|
2026
|
-
}[n];
|
|
2027
|
-
const range = [
|
|
2028
|
-
[-10, 10],
|
|
2029
|
-
[-10, 10],
|
|
2030
|
-
];
|
|
2031
|
-
|
|
2032
|
-
// @ts-expect-error - TS2322 - Type 'number[][][]' is not assignable to type 'readonly (readonly Coord[])[]'.
|
|
2033
|
-
return ys.map(function (y) {
|
|
2034
|
-
let segment = [
|
|
2035
|
-
[-5, y],
|
|
2036
|
-
[5, y],
|
|
2037
|
-
];
|
|
2038
|
-
// @ts-expect-error - TS4104 - The type 'readonly Coord[]' is 'readonly' and cannot be assigned to the mutable type 'number[][]'. | TS2345 - Argument of type 'number[][]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
2039
|
-
segment = InteractiveGraph.normalizeCoords(segment, range);
|
|
2040
|
-
// @ts-expect-error - TS4104 - The type 'readonly Coord[]' is 'readonly' and cannot be assigned to the mutable type 'number[][]'. | TS2345 - Argument of type 'number[][]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
2041
|
-
segment = InteractiveGraph.pointsFromNormalized(props, segment);
|
|
2042
|
-
return segment;
|
|
2043
|
-
});
|
|
2044
|
-
}
|
|
2045
|
-
|
|
2046
|
-
/**
|
|
2047
|
-
* @param {object} graph Like props.graph or props.correct
|
|
2048
|
-
* @param {object} props of an InteractiveGraph instance
|
|
2049
|
-
*/
|
|
2050
|
-
static getAngleCoords(
|
|
2051
|
-
graph: PerseusGraphTypeAngle,
|
|
2052
|
-
props: Props,
|
|
2053
|
-
): ReadonlyArray<Coord> {
|
|
2054
|
-
let coords = graph.coords;
|
|
2055
|
-
if (coords) {
|
|
2056
|
-
return coords;
|
|
2057
|
-
}
|
|
2058
|
-
|
|
2059
|
-
const snap = graph.snapDegrees || 1;
|
|
2060
|
-
let angle = snap;
|
|
2061
|
-
while (angle < 20) {
|
|
2062
|
-
angle += snap;
|
|
2063
|
-
}
|
|
2064
|
-
angle = (angle * Math.PI) / 180;
|
|
2065
|
-
const offset = ((graph.angleOffsetDeg || 0) * Math.PI) / 180;
|
|
2066
|
-
|
|
2067
|
-
coords = InteractiveGraph.pointsFromNormalized(props, [
|
|
2068
|
-
[0.85, 0.5],
|
|
2069
|
-
[0.5, 0.5],
|
|
2070
|
-
]);
|
|
2071
|
-
|
|
2072
|
-
// @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.
|
|
2073
|
-
const radius = magnitude(vector(...coords));
|
|
2074
|
-
|
|
2075
|
-
// Adjust the lower point by angleOffsetDeg degrees
|
|
2076
|
-
// @ts-expect-error - TS2542 - Index signature in type 'readonly Coord[]' only permits reading.
|
|
2077
|
-
coords[0] = [
|
|
2078
|
-
coords[1][0] + radius * Math.cos(offset),
|
|
2079
|
-
coords[1][1] + radius * Math.sin(offset),
|
|
2080
|
-
];
|
|
2081
|
-
// Position the upper point angle radians from the
|
|
2082
|
-
// lower point
|
|
2083
|
-
// @ts-expect-error - TS2542 - Index signature in type 'readonly Coord[]' only permits reading.
|
|
2084
|
-
coords[2] = [
|
|
2085
|
-
coords[1][0] + radius * Math.cos(angle + offset),
|
|
2086
|
-
coords[1][1] + radius * Math.sin(angle + offset),
|
|
2087
|
-
];
|
|
2088
|
-
|
|
2089
|
-
return coords;
|
|
2090
|
-
}
|
|
2091
|
-
|
|
2092
|
-
static normalizeCoords(
|
|
2093
|
-
coordsList: ReadonlyArray<Coord>,
|
|
2094
|
-
ranges: [Range, Range],
|
|
2095
|
-
): ReadonlyArray<Coord> {
|
|
2096
|
-
// @ts-expect-error - TS2322 - Type 'number[][]' is not assignable to type 'readonly Coord[]'.
|
|
2097
|
-
return _.map(coordsList, function (coords) {
|
|
2098
|
-
return _.map(coords, function (coord, i) {
|
|
2099
|
-
const extent = ranges[i][1] - ranges[i][0];
|
|
2100
|
-
return (coord + ranges[i][1]) / extent;
|
|
2101
|
-
});
|
|
2102
|
-
});
|
|
2103
|
-
}
|
|
2104
|
-
|
|
2105
|
-
static getEquationString(props: Props): string {
|
|
2106
|
-
const type = props.graph.type;
|
|
2107
|
-
const funcName = "get" + capitalize(type) + "EquationString";
|
|
2108
|
-
return InteractiveGraph[funcName](props);
|
|
2109
|
-
}
|
|
2110
|
-
|
|
2111
|
-
static pointsFromNormalized(
|
|
2112
|
-
props: Props,
|
|
2113
|
-
coordsList: ReadonlyArray<Coord>,
|
|
2114
|
-
noSnap?: boolean,
|
|
2115
|
-
): ReadonlyArray<Coord> {
|
|
2116
|
-
// @ts-expect-error - TS2322 - Type 'number[][]' is not assignable to type 'readonly Coord[]'.
|
|
2117
|
-
return _.map(coordsList, function (coords) {
|
|
2118
|
-
return _.map(coords, function (coord, i) {
|
|
2119
|
-
const range: Range = props.range[i];
|
|
2120
|
-
if (noSnap) {
|
|
2121
|
-
return range[0] + (range[1] - range[0]) * coord;
|
|
2122
|
-
}
|
|
2123
|
-
const step = props.step[i];
|
|
2124
|
-
const nSteps = numSteps(range, step);
|
|
2125
|
-
const tick = Math.round(coord * nSteps);
|
|
2126
|
-
return range[0] + step * tick;
|
|
2127
|
-
});
|
|
2128
|
-
});
|
|
2129
|
-
}
|
|
2130
|
-
|
|
2131
|
-
static getLinearEquationString(props: Props): string {
|
|
2132
|
-
const coords = InteractiveGraph.getLineCoords(props.graph, props);
|
|
2133
|
-
if (eq(coords[0][0], coords[1][0])) {
|
|
2134
|
-
return "x = " + coords[0][0].toFixed(3);
|
|
2135
|
-
}
|
|
2136
|
-
const m = (coords[1][1] - coords[0][1]) / (coords[1][0] - coords[0][0]);
|
|
2137
|
-
const b = coords[0][1] - m * coords[0][0];
|
|
2138
|
-
if (eq(m, 0)) {
|
|
2139
|
-
return "y = " + b.toFixed(3);
|
|
2140
|
-
}
|
|
2141
|
-
return "y = " + m.toFixed(3) + "x + " + b.toFixed(3);
|
|
2142
|
-
}
|
|
2143
|
-
|
|
2144
|
-
static getCurrentQuadraticCoefficients(props: Props): QuadraticCoefficient {
|
|
2145
|
-
// TODO(alpert): Don't duplicate
|
|
2146
|
-
const coords =
|
|
2147
|
-
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
2148
|
-
props.graph.coords ||
|
|
2149
|
-
InteractiveGraph.defaultQuadraticCoords(props);
|
|
2150
|
-
return InteractiveGraph.getQuadraticCoefficients(coords);
|
|
2151
|
-
}
|
|
2152
|
-
|
|
2153
|
-
static defaultQuadraticCoords(props: Props): ReadonlyArray<Coord> {
|
|
2154
|
-
const coords = [
|
|
2155
|
-
[0.25, 0.75],
|
|
2156
|
-
[0.5, 0.25],
|
|
2157
|
-
[0.75, 0.75],
|
|
2158
|
-
];
|
|
2159
|
-
// @ts-expect-error - TS2345 - Argument of type 'number[][]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
2160
|
-
return InteractiveGraph.pointsFromNormalized(props, coords);
|
|
2161
|
-
}
|
|
2162
|
-
|
|
2163
|
-
static getQuadraticEquationString(props: Props): string {
|
|
2164
|
-
const coeffs = InteractiveGraph.getCurrentQuadraticCoefficients(props);
|
|
2165
|
-
return (
|
|
2166
|
-
"y = " +
|
|
2167
|
-
coeffs[0].toFixed(3) +
|
|
2168
|
-
"x^2 + " +
|
|
2169
|
-
coeffs[1].toFixed(3) +
|
|
2170
|
-
"x + " +
|
|
2171
|
-
coeffs[2].toFixed(3)
|
|
2172
|
-
);
|
|
2173
|
-
}
|
|
2174
|
-
|
|
2175
|
-
static getCurrentSinusoidCoefficients(props: Props): SineCoefficient {
|
|
2176
|
-
const coords =
|
|
2177
|
-
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'.
|
|
2178
|
-
props.graph.coords || InteractiveGraph.defaultSinusoidCoords(props);
|
|
2179
|
-
return InteractiveGraph.getSinusoidCoefficients(coords);
|
|
2180
|
-
}
|
|
2181
|
-
|
|
2182
|
-
static defaultSinusoidCoords(props: Props): ReadonlyArray<Coord> {
|
|
2183
|
-
const coords = [
|
|
2184
|
-
[0.5, 0.5],
|
|
2185
|
-
[0.65, 0.6],
|
|
2186
|
-
];
|
|
2187
|
-
// @ts-expect-error - TS2345 - Argument of type 'number[][]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
2188
|
-
return InteractiveGraph.pointsFromNormalized(props, coords);
|
|
2189
|
-
}
|
|
2190
|
-
|
|
2191
|
-
static getSinusoidEquationString(props: Props): string {
|
|
2192
|
-
const coeffs = InteractiveGraph.getCurrentSinusoidCoefficients(props);
|
|
2193
|
-
return (
|
|
2194
|
-
"y = " +
|
|
2195
|
-
coeffs[0].toFixed(3) +
|
|
2196
|
-
"sin(" +
|
|
2197
|
-
coeffs[1].toFixed(3) +
|
|
2198
|
-
"x - " +
|
|
2199
|
-
coeffs[2].toFixed(3) +
|
|
2200
|
-
") + " +
|
|
2201
|
-
coeffs[3].toFixed(3)
|
|
2202
|
-
);
|
|
2203
|
-
}
|
|
2204
|
-
|
|
2205
|
-
static getCircleEquationString(props: Props): string {
|
|
2206
|
-
const graph = props.graph;
|
|
2207
|
-
// TODO(alpert): Don't duplicate
|
|
2208
|
-
// @ts-expect-error - TS2339 - Property 'center' does not exist on type 'PerseusGraphType'.
|
|
2209
|
-
const center = graph.center || [0, 0];
|
|
2210
|
-
// @ts-expect-error - TS2339 - Property 'radius' does not exist on type 'PerseusGraphType'.
|
|
2211
|
-
const radius = graph.radius || 2;
|
|
2212
|
-
return (
|
|
2213
|
-
"center (" + center[0] + ", " + center[1] + "), radius " + radius
|
|
2214
|
-
);
|
|
2215
|
-
}
|
|
2216
|
-
|
|
2217
|
-
static getLinearSystemEquationString(props: Props): string {
|
|
2218
|
-
const coords = InteractiveGraph.getLinearSystemCoords(
|
|
2219
|
-
props.graph,
|
|
2220
|
-
props,
|
|
2221
|
-
);
|
|
2222
|
-
|
|
2223
|
-
return (
|
|
2224
|
-
"\n" +
|
|
2225
|
-
getLineEquation(coords[0][0], coords[0][1]) +
|
|
2226
|
-
"\n" +
|
|
2227
|
-
getLineEquation(coords[1][0], coords[1][1]) +
|
|
2228
|
-
"\n" +
|
|
2229
|
-
getLineIntersection(coords[0], coords[1])
|
|
2230
|
-
);
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
|
-
static getPointEquationString(props: Props): string {
|
|
2234
|
-
if (props.graph.type !== "point") {
|
|
2235
|
-
throw makeInvalidTypeError("getPointEquationString", "point");
|
|
2236
|
-
}
|
|
2237
|
-
|
|
2238
|
-
const coords = InteractiveGraph.getPointCoords(props.graph, props);
|
|
2239
|
-
return coords
|
|
2240
|
-
.map(function (coord) {
|
|
2241
|
-
return "(" + coord[0] + ", " + coord[1] + ")";
|
|
2242
|
-
})
|
|
2243
|
-
.join(", ");
|
|
2244
|
-
}
|
|
2245
|
-
|
|
2246
|
-
static getSegmentEquationString(props: Props): string {
|
|
2247
|
-
if (props.graph.type !== "segment") {
|
|
2248
|
-
throw makeInvalidTypeError("getSegmentEquationString", "segment");
|
|
2249
|
-
}
|
|
2250
|
-
|
|
2251
|
-
const segments = InteractiveGraph.getSegmentCoords(props.graph, props);
|
|
2252
|
-
return _.map(segments, function (segment) {
|
|
2253
|
-
return (
|
|
2254
|
-
"[" +
|
|
2255
|
-
_.map(segment, function (coord) {
|
|
2256
|
-
return "(" + coord.join(", ") + ")";
|
|
2257
|
-
}).join(" ") +
|
|
2258
|
-
"]"
|
|
2259
|
-
);
|
|
2260
|
-
}).join(" ");
|
|
2261
|
-
}
|
|
2262
|
-
|
|
2263
|
-
static getRayEquationString(props: Props): string {
|
|
2264
|
-
if (props.graph.type !== "ray") {
|
|
2265
|
-
throw makeInvalidTypeError("createPointForPolygonType", "ray");
|
|
2266
|
-
}
|
|
2267
|
-
|
|
2268
|
-
const coords = InteractiveGraph.getLineCoords(props.graph, props);
|
|
2269
|
-
const a = coords[0];
|
|
2270
|
-
const b = coords[1];
|
|
2271
|
-
let eq = InteractiveGraph.getLinearEquationString(props);
|
|
2272
|
-
|
|
2273
|
-
if (a[0] > b[0]) {
|
|
2274
|
-
eq += " (for x <= " + a[0].toFixed(3) + ")";
|
|
2275
|
-
} else if (a[0] < b[0]) {
|
|
2276
|
-
eq += " (for x >= " + a[0].toFixed(3) + ")";
|
|
2277
|
-
} else if (a[1] > b[1]) {
|
|
2278
|
-
eq += " (for y <= " + a[1].toFixed(3) + ")";
|
|
2279
|
-
} else {
|
|
2280
|
-
eq += " (for y >= " + a[1].toFixed(3) + ")";
|
|
2281
|
-
}
|
|
2282
|
-
|
|
2283
|
-
return eq;
|
|
2284
|
-
}
|
|
2285
|
-
|
|
2286
|
-
static getPolygonEquationString(props: Props): string {
|
|
2287
|
-
if (props.graph.type !== "polygon") {
|
|
2288
|
-
throw makeInvalidTypeError("getPolygonEquationString", "polygon");
|
|
2289
|
-
}
|
|
2290
|
-
const coords = InteractiveGraph.getPolygonCoords(props.graph, props);
|
|
2291
|
-
return _.map(coords, function (coord) {
|
|
2292
|
-
return "(" + coord.join(", ") + ")";
|
|
2293
|
-
}).join(" ");
|
|
2294
|
-
}
|
|
2295
|
-
|
|
2296
|
-
static getAngleEquationString(props: Props): string {
|
|
2297
|
-
if (props.graph.type !== "angle") {
|
|
2298
|
-
throw makeInvalidTypeError("getAngleEquationString", "angle");
|
|
2299
|
-
}
|
|
2300
|
-
const coords = InteractiveGraph.getAngleCoords(props.graph, props);
|
|
2301
|
-
const angle = GraphUtils.findAngle(coords[2], coords[0], coords[1]);
|
|
2302
|
-
return (
|
|
2303
|
-
angle.toFixed(0) +
|
|
2304
|
-
"\u00B0 angle" +
|
|
2305
|
-
" at (" +
|
|
2306
|
-
coords[1].join(", ") +
|
|
2307
|
-
")"
|
|
2308
|
-
);
|
|
2309
|
-
}
|
|
2310
|
-
|
|
2311
|
-
static validate(
|
|
2312
|
-
// TODO(kevinb): rename state to userInput
|
|
2313
|
-
// state === userInput
|
|
2314
|
-
state: PerseusGraphType,
|
|
2315
|
-
rubric: Rubric,
|
|
2316
|
-
component: any,
|
|
2317
|
-
): PerseusScore {
|
|
2318
|
-
// When nothing has moved, there will neither be coords nor the
|
|
2319
|
-
// circle's center/radius fields. When those fields are absent, skip
|
|
2320
|
-
// all these checks; just go mark the answer as empty.
|
|
2321
|
-
// @ts-expect-error - TS2339 - Property 'coords' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'center' does not exist on type 'PerseusGraphType'. | TS2339 - Property 'radius' does not exist on type 'PerseusGraphType'.
|
|
2322
|
-
const hasValue = !!(state.coords || (state.center && state.radius));
|
|
2323
|
-
|
|
2324
|
-
if (state.type === rubric.correct.type && hasValue) {
|
|
2325
|
-
if (
|
|
2326
|
-
state.type === "linear" &&
|
|
2327
|
-
rubric.correct.type === "linear" &&
|
|
2328
|
-
state.coords != null
|
|
2329
|
-
) {
|
|
2330
|
-
const guess = state.coords;
|
|
2331
|
-
const correct = rubric.correct.coords;
|
|
2332
|
-
|
|
2333
|
-
// If both of the guess points are on the correct line, it's
|
|
2334
|
-
// correct.
|
|
2335
|
-
if (
|
|
2336
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
2337
|
-
collinear(correct[0], correct[1], guess[0]) &&
|
|
2338
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
2339
|
-
collinear(correct[0], correct[1], guess[1])
|
|
2340
|
-
) {
|
|
2341
|
-
return {
|
|
2342
|
-
type: "points",
|
|
2343
|
-
earned: 1,
|
|
2344
|
-
total: 1,
|
|
2345
|
-
message: null,
|
|
2346
|
-
};
|
|
2347
|
-
}
|
|
2348
|
-
} else if (
|
|
2349
|
-
state.type === "linear-system" &&
|
|
2350
|
-
rubric.correct.type === "linear-system" &&
|
|
2351
|
-
state.coords != null
|
|
2352
|
-
) {
|
|
2353
|
-
const guess = state.coords;
|
|
2354
|
-
const correct = rubric.correct.coords as ReadonlyArray<
|
|
2355
|
-
ReadonlyArray<Coord>
|
|
2356
|
-
>;
|
|
2357
|
-
|
|
2358
|
-
if (
|
|
2359
|
-
(collinear(correct[0][0], correct[0][1], guess[0][0]) &&
|
|
2360
|
-
collinear(correct[0][0], correct[0][1], guess[0][1]) &&
|
|
2361
|
-
collinear(correct[1][0], correct[1][1], guess[1][0]) &&
|
|
2362
|
-
collinear(correct[1][0], correct[1][1], guess[1][1])) ||
|
|
2363
|
-
(collinear(correct[0][0], correct[0][1], guess[1][0]) &&
|
|
2364
|
-
collinear(correct[0][0], correct[0][1], guess[1][1]) &&
|
|
2365
|
-
collinear(correct[1][0], correct[1][1], guess[0][0]) &&
|
|
2366
|
-
collinear(correct[1][0], correct[1][1], guess[0][1]))
|
|
2367
|
-
) {
|
|
2368
|
-
return {
|
|
2369
|
-
type: "points",
|
|
2370
|
-
earned: 1,
|
|
2371
|
-
total: 1,
|
|
2372
|
-
message: null,
|
|
2373
|
-
};
|
|
2374
|
-
}
|
|
2375
|
-
} else if (
|
|
2376
|
-
state.type === "quadratic" &&
|
|
2377
|
-
rubric.correct.type === "quadratic" &&
|
|
2378
|
-
state.coords != null
|
|
2379
|
-
) {
|
|
2380
|
-
// If the parabola coefficients match, it's correct.
|
|
2381
|
-
const guessCoeffs = this.getQuadraticCoefficients(state.coords);
|
|
2382
|
-
const correctCoeffs = this.getQuadraticCoefficients(
|
|
2383
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
2384
|
-
rubric.correct.coords,
|
|
2385
|
-
);
|
|
2386
|
-
if (deepEq(guessCoeffs, correctCoeffs)) {
|
|
2387
|
-
return {
|
|
2388
|
-
type: "points",
|
|
2389
|
-
earned: 1,
|
|
2390
|
-
total: 1,
|
|
2391
|
-
message: null,
|
|
2392
|
-
};
|
|
2393
|
-
}
|
|
2394
|
-
} else if (
|
|
2395
|
-
state.type === "sinusoid" &&
|
|
2396
|
-
rubric.correct.type === "sinusoid" &&
|
|
2397
|
-
state.coords != null
|
|
2398
|
-
) {
|
|
2399
|
-
const guessCoeffs = this.getSinusoidCoefficients(state.coords);
|
|
2400
|
-
const correctCoeffs = this.getSinusoidCoefficients(
|
|
2401
|
-
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
2402
|
-
rubric.correct.coords,
|
|
2403
|
-
);
|
|
2404
|
-
|
|
2405
|
-
const canonicalGuessCoeffs =
|
|
2406
|
-
canonicalSineCoefficients(guessCoeffs);
|
|
2407
|
-
const canonicalCorrectCoeffs =
|
|
2408
|
-
canonicalSineCoefficients(correctCoeffs);
|
|
2409
|
-
// If the canonical coefficients match, it's correct.
|
|
2410
|
-
if (deepEq(canonicalGuessCoeffs, canonicalCorrectCoeffs)) {
|
|
2411
|
-
return {
|
|
2412
|
-
type: "points",
|
|
2413
|
-
earned: 1,
|
|
2414
|
-
total: 1,
|
|
2415
|
-
message: null,
|
|
2416
|
-
};
|
|
2417
|
-
}
|
|
2418
|
-
} else if (
|
|
2419
|
-
state.type === "circle" &&
|
|
2420
|
-
rubric.correct.type === "circle"
|
|
2421
|
-
) {
|
|
2422
|
-
if (
|
|
2423
|
-
deepEq(state.center, rubric.correct.center) &&
|
|
2424
|
-
eq(state.radius, rubric.correct.radius)
|
|
2425
|
-
) {
|
|
2426
|
-
return {
|
|
2427
|
-
type: "points",
|
|
2428
|
-
earned: 1,
|
|
2429
|
-
total: 1,
|
|
2430
|
-
message: null,
|
|
2431
|
-
};
|
|
2432
|
-
}
|
|
2433
|
-
} else if (
|
|
2434
|
-
state.type === "point" &&
|
|
2435
|
-
rubric.correct.type === "point" &&
|
|
2436
|
-
state.coords != null
|
|
2437
|
-
) {
|
|
2438
|
-
let correct = InteractiveGraph.getPointCoords(
|
|
2439
|
-
rubric.correct,
|
|
2440
|
-
component,
|
|
2441
|
-
);
|
|
2442
|
-
const guess = state.coords?.slice();
|
|
2443
|
-
correct = correct.slice();
|
|
2444
|
-
// Everything's already rounded so we shouldn't need to do an
|
|
2445
|
-
// eq() comparison but _.isEqual(0, -0) is false, so we'll use
|
|
2446
|
-
// eq() anyway. The sort should be fine because it'll stringify
|
|
2447
|
-
// it and -0 converted to a string is "0"
|
|
2448
|
-
guess?.sort();
|
|
2449
|
-
// @ts-expect-error - TS2339 - Property 'sort' does not exist on type 'readonly Coord[]'.
|
|
2450
|
-
correct.sort();
|
|
2451
|
-
if (deepEq(guess, correct)) {
|
|
2452
|
-
return {
|
|
2453
|
-
type: "points",
|
|
2454
|
-
earned: 1,
|
|
2455
|
-
total: 1,
|
|
2456
|
-
message: null,
|
|
2457
|
-
};
|
|
2458
|
-
}
|
|
2459
|
-
} else if (
|
|
2460
|
-
state.type === "polygon" &&
|
|
2461
|
-
rubric.correct.type === "polygon" &&
|
|
2462
|
-
state.coords != null
|
|
2463
|
-
) {
|
|
2464
|
-
const guess: Array<Coord> = state.coords?.slice();
|
|
2465
|
-
// @ts-expect-error - TS2322 - Type 'Coord[] | undefined' is not assignable to type 'Coord[]'.
|
|
2466
|
-
const correct: Array<Coord> = rubric.correct.coords?.slice();
|
|
2467
|
-
|
|
2468
|
-
let match;
|
|
2469
|
-
if (rubric.correct.match === "similar") {
|
|
2470
|
-
match = similar(guess, correct, Number.POSITIVE_INFINITY);
|
|
2471
|
-
} else if (rubric.correct.match === "congruent") {
|
|
2472
|
-
match = similar(guess, correct, knumber.DEFAULT_TOLERANCE);
|
|
2473
|
-
} else if (rubric.correct.match === "approx") {
|
|
2474
|
-
match = similar(guess, correct, 0.1);
|
|
2475
|
-
} else {
|
|
2476
|
-
/* exact */
|
|
2477
|
-
guess.sort();
|
|
2478
|
-
correct.sort();
|
|
2479
|
-
match = deepEq(guess, correct);
|
|
2480
|
-
}
|
|
2481
|
-
|
|
2482
|
-
if (match) {
|
|
2483
|
-
return {
|
|
2484
|
-
type: "points",
|
|
2485
|
-
earned: 1,
|
|
2486
|
-
total: 1,
|
|
2487
|
-
message: null,
|
|
2488
|
-
};
|
|
2489
|
-
}
|
|
2490
|
-
} else if (
|
|
2491
|
-
state.type === "segment" &&
|
|
2492
|
-
rubric.correct.type === "segment" &&
|
|
2493
|
-
state.coords != null
|
|
2494
|
-
) {
|
|
2495
|
-
let guess = state.coords.slice();
|
|
2496
|
-
let correct = rubric.correct.coords?.slice();
|
|
2497
|
-
guess = _.invoke(guess, "sort").sort();
|
|
2498
|
-
// @ts-expect-error - TS2345 - Argument of type '(readonly Coord[])[] | undefined' is not assignable to parameter of type 'Collection<any>'.
|
|
2499
|
-
correct = _.invoke(correct, "sort").sort();
|
|
2500
|
-
if (deepEq(guess, correct)) {
|
|
2501
|
-
return {
|
|
2502
|
-
type: "points",
|
|
2503
|
-
earned: 1,
|
|
2504
|
-
total: 1,
|
|
2505
|
-
message: null,
|
|
2506
|
-
};
|
|
2507
|
-
}
|
|
2508
|
-
} else if (
|
|
2509
|
-
state.type === "ray" &&
|
|
2510
|
-
rubric.correct.type === "ray" &&
|
|
2511
|
-
state.coords != null
|
|
2512
|
-
) {
|
|
2513
|
-
const guess = state.coords;
|
|
2514
|
-
const correct = rubric.correct.coords;
|
|
2515
|
-
if (
|
|
2516
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
2517
|
-
deepEq(guess[0], correct[0]) &&
|
|
2518
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
2519
|
-
collinear(correct[0], correct[1], guess[1])
|
|
2520
|
-
) {
|
|
2521
|
-
return {
|
|
2522
|
-
type: "points",
|
|
2523
|
-
earned: 1,
|
|
2524
|
-
total: 1,
|
|
2525
|
-
message: null,
|
|
2526
|
-
};
|
|
2527
|
-
}
|
|
2528
|
-
} else if (
|
|
2529
|
-
state.type === "angle" &&
|
|
2530
|
-
rubric.correct.type === "angle"
|
|
2531
|
-
) {
|
|
2532
|
-
const guess = state.coords;
|
|
2533
|
-
const correct = rubric.correct.coords;
|
|
2534
|
-
|
|
2535
|
-
let match;
|
|
2536
|
-
if (rubric.correct.match === "congruent") {
|
|
2537
|
-
const angles = _.map([guess, correct], function (coords) {
|
|
2538
|
-
const angle = GraphUtils.findAngle(
|
|
2539
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
2540
|
-
coords[2],
|
|
2541
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
2542
|
-
coords[0],
|
|
2543
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
2544
|
-
coords[1],
|
|
2545
|
-
);
|
|
2546
|
-
return (angle + 360) % 360;
|
|
2547
|
-
});
|
|
2548
|
-
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
2549
|
-
match = eq(...angles);
|
|
2550
|
-
} else {
|
|
2551
|
-
/* exact */
|
|
2552
|
-
match = // @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
2553
|
-
deepEq(guess[1], correct[1]) &&
|
|
2554
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
2555
|
-
collinear(correct[1], correct[0], guess[0]) &&
|
|
2556
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
2557
|
-
collinear(correct[1], correct[2], guess[2]);
|
|
2558
|
-
}
|
|
2559
|
-
|
|
2560
|
-
if (match) {
|
|
2561
|
-
return {
|
|
2562
|
-
type: "points",
|
|
2563
|
-
earned: 1,
|
|
2564
|
-
total: 1,
|
|
2565
|
-
message: null,
|
|
2566
|
-
};
|
|
2567
|
-
}
|
|
2568
|
-
}
|
|
2569
|
-
}
|
|
2570
|
-
|
|
2571
|
-
// The input wasn't correct, so check if it's a blank input or if it's
|
|
2572
|
-
// actually just wrong
|
|
2573
|
-
if (!hasValue || _.isEqual(state, rubric.graph)) {
|
|
2574
|
-
// We're where we started.
|
|
2575
|
-
return {
|
|
2576
|
-
type: "invalid",
|
|
2577
|
-
message: null,
|
|
2578
|
-
};
|
|
2579
|
-
}
|
|
2580
|
-
return {
|
|
2581
|
-
type: "points",
|
|
2582
|
-
earned: 0,
|
|
2583
|
-
total: 1,
|
|
2584
|
-
message: null,
|
|
2585
|
-
};
|
|
2586
|
-
}
|
|
2587
|
-
|
|
2588
|
-
static getUserInputFromProps(props: Props): PerseusGraphType {
|
|
2589
|
-
return props.graph;
|
|
2590
|
-
}
|
|
2591
|
-
}
|
|
2592
|
-
|
|
2593
|
-
export default {
|
|
2594
|
-
name: "interactive-graph",
|
|
2595
|
-
displayName: "Interactive graph",
|
|
2596
|
-
widget: InteractiveGraph,
|
|
2597
|
-
} as WidgetExports<typeof InteractiveGraph>;
|