@khanacademy/perseus 2.1.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc.js +1 -1
- package/.eslintrc.js +1 -0
- package/CHANGELOG.md +22 -0
- package/dist/__tests__/mock-asset-loading-widget.d.ts +59 -0
- package/dist/__tests__/mock-asset-loading-widget.js.flow +70 -0
- package/dist/__tests__/mock-widget.d.ts +30 -0
- package/dist/__tests__/mock-widget.js.flow +39 -0
- package/dist/__tests__/test-items/image-item.d.ts +14 -0
- package/dist/__tests__/test-items/image-item.js.flow +20 -0
- package/dist/__tests__/test-items/input-number-1-item.d.ts +9 -0
- package/dist/__tests__/test-items/input-number-1-item.js.flow +15 -0
- package/dist/__tests__/test-items/input-number-2-item.d.ts +9 -0
- package/dist/__tests__/test-items/input-number-2-item.js.flow +15 -0
- package/dist/__tests__/test-items/table-item.d.ts +9 -0
- package/dist/__tests__/test-items/table-item.js.flow +15 -0
- package/dist/a11y.d.ts +5 -0
- package/dist/a11y.js.flow +11 -0
- package/dist/all-widgets.d.ts +22 -0
- package/dist/all-widgets.js.flow +33 -0
- package/dist/article-renderer.d.ts +54 -0
- package/dist/article-renderer.js.flow +69 -0
- package/dist/asset-context.d.ts +14 -0
- package/dist/asset-context.js.flow +15 -0
- package/dist/basic-widgets.d.ts +22 -0
- package/dist/basic-widgets.js.flow +33 -0
- package/dist/components/button-group.d.ts +27 -0
- package/dist/components/button-group.js.flow +33 -0
- package/dist/components/constants.d.ts +7 -0
- package/dist/components/constants.js.flow +13 -0
- package/dist/components/fixed-to-responsive.d.ts +2 -0
- package/dist/components/fixed-to-responsive.js.flow +8 -0
- package/dist/components/graph.d.ts +44 -0
- package/dist/components/graph.js.flow +52 -0
- package/dist/components/graphie-classes.d.ts +13 -0
- package/dist/components/graphie-classes.js.flow +19 -0
- package/dist/components/graphie-movables.d.ts +16 -0
- package/dist/components/graphie-movables.js.flow +22 -0
- package/dist/components/graphie.d.ts +44 -0
- package/dist/components/graphie.js.flow +56 -0
- package/dist/components/highlighting/highlightable-content.d.ts +39 -0
- package/dist/components/highlighting/highlightable-content.js.flow +59 -0
- package/dist/components/highlighting/highlights.d.ts +30 -0
- package/dist/components/highlighting/highlights.js.flow +54 -0
- package/dist/components/highlighting/ranges.d.ts +42 -0
- package/dist/components/highlighting/ranges.js.flow +60 -0
- package/dist/components/highlighting/types.d.ts +64 -0
- package/dist/components/highlighting/types.js.flow +70 -0
- package/dist/components/highlighting/ui/highlight-renderer.d.ts +34 -0
- package/dist/components/highlighting/ui/highlight-renderer.js.flow +45 -0
- package/dist/components/highlighting/ui/highlight-set-renderer.d.ts +34 -0
- package/dist/components/highlighting/ui/highlight-set-renderer.js.flow +44 -0
- package/dist/components/highlighting/ui/highlight-tooltip.d.ts +16 -0
- package/dist/components/highlighting/ui/highlight-tooltip.js.flow +24 -0
- package/dist/components/highlighting/ui/highlighting-ui.d.ts +19 -0
- package/dist/components/highlighting/ui/highlighting-ui.js.flow +30 -0
- package/dist/components/highlighting/ui/selection-tracker.d.ts +47 -0
- package/dist/components/highlighting/ui/selection-tracker.js.flow +56 -0
- package/dist/components/highlighting/ui/types.d.ts +13 -0
- package/dist/components/highlighting/ui/types.js.flow +22 -0
- package/dist/components/highlighting/ui/util.d.ts +28 -0
- package/dist/components/highlighting/ui/util.js.flow +42 -0
- package/dist/components/highlighting/word-indexer.d.ts +28 -0
- package/dist/components/highlighting/word-indexer.js.flow +21 -0
- package/dist/components/hud.d.ts +15 -0
- package/dist/components/hud.js.flow +21 -0
- package/dist/components/icon.d.ts +56 -0
- package/dist/components/icon.js.flow +31 -0
- package/dist/components/image-loader.d.ts +52 -0
- package/dist/components/image-loader.js.flow +49 -0
- package/dist/components/info-tip/info-tip.d.ts +14 -0
- package/dist/components/info-tip/info-tip.js.flow +20 -0
- package/dist/components/info-tip.d.ts +18 -0
- package/dist/components/info-tip.js.flow +18 -0
- package/dist/components/inline-icon.d.ts +10 -0
- package/dist/components/inline-icon.js.flow +16 -0
- package/dist/components/input-with-examples.d.ts +52 -0
- package/dist/components/input-with-examples.js.flow +58 -0
- package/dist/components/lint.d.ts +23 -0
- package/dist/components/lint.js.flow +29 -0
- package/dist/components/math-input.d.ts +40 -0
- package/dist/components/math-input.js.flow +46 -0
- package/dist/components/math-output.d.ts +23 -0
- package/dist/components/math-output.js.flow +29 -0
- package/dist/components/multi-button-group.d.ts +29 -0
- package/dist/components/multi-button-group.js.flow +35 -0
- package/dist/components/number-input.d.ts +34 -0
- package/dist/components/number-input.js.flow +42 -0
- package/dist/components/prop-check-box.d.ts +2 -0
- package/dist/components/prop-check-box.js.flow +8 -0
- package/dist/components/range-input.d.ts +14 -0
- package/dist/components/range-input.js.flow +20 -0
- package/dist/components/simple-keypad-input.d.ts +19 -0
- package/dist/components/simple-keypad-input.js.flow +16 -0
- package/dist/components/sortable.d.ts +68 -0
- package/dist/components/sortable.js.flow +78 -0
- package/dist/components/stub-tag-editor.d.ts +25 -0
- package/dist/components/stub-tag-editor.js.flow +18 -0
- package/dist/components/svg-image.d.ts +74 -0
- package/dist/components/svg-image.js.flow +83 -0
- package/dist/components/tex-buttons.d.ts +20 -0
- package/dist/components/tex-buttons.js.flow +26 -0
- package/dist/components/tex.d.ts +25 -0
- package/dist/components/tex.js.flow +25 -0
- package/dist/components/text-input.d.ts +33 -0
- package/dist/components/text-input.js.flow +39 -0
- package/dist/components/text-list-editor.d.ts +16 -0
- package/dist/components/text-list-editor.js.flow +22 -0
- package/dist/components/tooltip.d.ts +32 -0
- package/dist/components/tooltip.js.flow +38 -0
- package/dist/components/visibility-observer/create-visibility-observer.d.ts +34 -0
- package/dist/components/visibility-observer/create-visibility-observer.js.flow +23 -0
- package/dist/components/visibility-observer/get-scroll-parent.d.ts +1 -0
- package/dist/components/visibility-observer/get-scroll-parent.js.flow +7 -0
- package/dist/components/visibility-observer/visibility-observer.d.ts +31 -0
- package/dist/components/visibility-observer/visibility-observer.js.flow +36 -0
- package/dist/components/zoomable-tex.d.ts +18 -0
- package/dist/components/zoomable-tex.js.flow +18 -0
- package/dist/components/zoomable.d.ts +64 -0
- package/dist/components/zoomable.js.flow +70 -0
- package/dist/components.d.ts +16 -0
- package/dist/components.js.flow +22 -0
- package/dist/definition-context.d.ts +21 -0
- package/dist/definition-context.js.flow +25 -0
- package/dist/dependencies.d.ts +3 -0
- package/dist/dependencies.js.flow +9 -0
- package/dist/error-boundary.d.ts +13 -0
- package/dist/error-boundary.js.flow +21 -0
- package/dist/es/index.js +23239 -26937
- package/dist/es/index.js.map +1 -1
- package/dist/extra-widgets.d.ts +22 -0
- package/dist/extra-widgets.js.flow +33 -0
- package/dist/hint-renderer.d.ts +23 -0
- package/dist/hint-renderer.js.flow +32 -0
- package/dist/hints-renderer.d.ts +29 -0
- package/dist/hints-renderer.js.flow +41 -0
- package/dist/icon-paths.d.ts +117 -0
- package/dist/icon-paths.js.flow +123 -0
- package/dist/index.d.ts +103 -2
- package/dist/index.js +13063 -16571
- package/dist/index.js.flow +133 -2
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +14 -0
- package/dist/init.js.flow +20 -0
- package/dist/interaction-tracker.d.ts +23 -0
- package/dist/interaction-tracker.js.flow +33 -0
- package/dist/interactive2/__tests__/movable-mock.d.ts +5 -0
- package/dist/interactive2/__tests__/movable-mock.js.flow +11 -0
- package/dist/interactive2/interactive-util.d.ts +31 -0
- package/dist/interactive2/interactive-util.js.flow +50 -0
- package/dist/interactive2/movable-helper-methods.d.ts +9 -0
- package/dist/interactive2/movable-helper-methods.js.flow +15 -0
- package/dist/interactive2/movable-line-options.d.ts +34 -0
- package/dist/interactive2/movable-line-options.js.flow +44 -0
- package/dist/interactive2/movable-line.d.ts +2 -0
- package/dist/interactive2/movable-line.js.flow +8 -0
- package/dist/interactive2/movable-point-options.d.ts +34 -0
- package/dist/interactive2/movable-point-options.js.flow +44 -0
- package/dist/interactive2/movable-point.d.ts +2 -0
- package/dist/interactive2/movable-point.js.flow +8 -0
- package/dist/interactive2/movable-polygon-options.d.ts +40 -0
- package/dist/interactive2/movable-polygon-options.js.flow +47 -0
- package/dist/interactive2/movable-polygon.d.ts +2 -0
- package/dist/interactive2/movable-polygon.js.flow +8 -0
- package/dist/interactive2/movable.d.ts +2 -0
- package/dist/interactive2/movable.js.flow +8 -0
- package/dist/interactive2/objective_.d.ts +13 -0
- package/dist/interactive2/objective_.js.flow +25 -0
- package/dist/interactive2/types.d.ts +2 -0
- package/dist/interactive2/types.js.flow +8 -0
- package/dist/interactive2/wrapped-defaults.d.ts +5 -0
- package/dist/interactive2/wrapped-defaults.js.flow +11 -0
- package/dist/interactive2/wrapped-ellipse.d.ts +3 -0
- package/dist/interactive2/wrapped-ellipse.js.flow +14 -0
- package/dist/interactive2/wrapped-line.d.ts +3 -0
- package/dist/interactive2/wrapped-line.js.flow +14 -0
- package/dist/interactive2/wrapped-path.d.ts +3 -0
- package/dist/interactive2/wrapped-path.js.flow +13 -0
- package/dist/interactive2.d.ts +10 -0
- package/dist/interactive2.js.flow +16 -0
- package/dist/item-renderer.d.ts +117 -0
- package/dist/item-renderer.js.flow +136 -0
- package/dist/item-version.d.ts +4 -0
- package/dist/item-version.js.flow +11 -0
- package/dist/jipt-paragraphs.d.ts +8 -0
- package/dist/jipt-paragraphs.js.flow +14 -0
- package/dist/keypad-context.d.ts +20 -0
- package/dist/keypad-context.js.flow +18 -0
- package/dist/loading-context.d.ts +14 -0
- package/dist/loading-context.js.flow +12 -0
- package/dist/logging/log.d.ts +55 -0
- package/dist/logging/log.js.flow +62 -0
- package/dist/mixins/changeable.d.ts +30 -0
- package/dist/mixins/changeable.js.flow +33 -0
- package/dist/mixins/editor-jsonify.d.ts +4 -0
- package/dist/mixins/editor-jsonify.js.flow +10 -0
- package/dist/mixins/provide-keypad.d.ts +36 -0
- package/dist/mixins/provide-keypad.js.flow +33 -0
- package/dist/mixins/widget-jsonify-deprecated.d.ts +5 -0
- package/dist/mixins/widget-jsonify-deprecated.js.flow +11 -0
- package/dist/mixins/widget-prop-denylist.d.ts +14 -0
- package/dist/mixins/widget-prop-denylist.js.flow +20 -0
- package/dist/multi-items/item-types.d.ts +32 -0
- package/dist/multi-items/items.d.ts +53 -0
- package/dist/multi-items/items.js.flow +66 -0
- package/dist/multi-items/multi-renderer.d.ts +152 -0
- package/dist/multi-items/multi-renderer.js.flow +213 -0
- package/dist/multi-items/prop-type-builders.d.ts +20 -0
- package/dist/multi-items/prop-type-builders.js.flow +22 -0
- package/dist/multi-items/shape-types.d.ts +47 -0
- package/dist/multi-items/shape-types.js.flow +60 -0
- package/dist/multi-items/shapes.d.ts +23 -0
- package/dist/multi-items/shapes.js.flow +25 -0
- package/dist/multi-items/tree-types.d.ts +34 -0
- package/dist/multi-items/tree-types.js.flow +45 -0
- package/dist/multi-items/trees.d.ts +146 -0
- package/dist/multi-items/trees.js.flow +147 -0
- package/dist/multi-items.d.ts +73 -0
- package/dist/multi-items.js.flow +36 -0
- package/dist/perseus-api.d.ts +128 -0
- package/dist/perseus-api.js.flow +139 -0
- package/dist/perseus-env.d.ts +3 -0
- package/dist/perseus-env.js.flow +6 -0
- package/dist/perseus-error.d.ts +11 -0
- package/dist/perseus-error.js.flow +16 -0
- package/dist/perseus-markdown.d.ts +11 -0
- package/dist/perseus-markdown.js.flow +20 -0
- package/dist/perseus-types.d.ts +776 -0
- package/dist/perseus-types.js.flow +1032 -0
- package/dist/question-paragraph.d.ts +11 -0
- package/dist/question-paragraph.js.flow +17 -0
- package/dist/renderability.d.ts +10 -0
- package/dist/renderability.js.flow +19 -0
- package/dist/renderer.d.ts +254 -0
- package/dist/renderer.js.flow +327 -0
- package/dist/server-item-renderer.d.ts +113 -0
- package/dist/server-item-renderer.js.flow +136 -0
- package/dist/sigfigs.d.ts +32 -0
- package/dist/sigfigs.js.flow +41 -0
- package/dist/styles/constants.d.ts +56 -0
- package/dist/styles/constants.js.flow +62 -0
- package/dist/styles/global-constants.d.ts +342 -0
- package/dist/styles/global-constants.js.flow +348 -0
- package/dist/styles/global-styles.d.ts +47 -0
- package/dist/styles/global-styles.js.flow +53 -0
- package/dist/styles/media-queries.d.ts +29 -0
- package/dist/styles/media-queries.js.flow +35 -0
- package/dist/styles/shared.d.ts +3 -0
- package/dist/styles/shared.js.flow +9 -0
- package/dist/tex-wrangler.d.ts +7 -0
- package/dist/tex-wrangler.js.flow +13 -0
- package/dist/translation-linter.d.ts +21 -0
- package/dist/translation-linter.js.flow +32 -0
- package/dist/traversal.d.ts +15 -0
- package/dist/traversal.js.flow +26 -0
- package/dist/types.d.ts +262 -0
- package/dist/types.js.flow +337 -0
- package/dist/util/a11y.d.ts +3 -0
- package/dist/util/a11y.js.flow +9 -0
- package/dist/util/answer-types.d.ts +34 -0
- package/dist/util/answer-types.js.flow +49 -0
- package/dist/util/colors.d.ts +89 -0
- package/dist/util/colors.js.flow +95 -0
- package/dist/util/fix-passage-refs.d.ts +2 -0
- package/dist/util/fix-passage-refs.js.flow +8 -0
- package/dist/util/graph-utils.d.ts +12 -0
- package/dist/util/graph-utils.js.flow +9 -0
- package/dist/util/graphie.d.ts +2 -0
- package/dist/util/graphie.js.flow +8 -0
- package/dist/util/interactive.d.ts +9 -0
- package/dist/util/interactive.js.flow +9 -0
- package/dist/util/katex-preprocess.d.ts +6 -0
- package/dist/util/katex-preprocess.js.flow +12 -0
- package/dist/util/math.d.ts +28 -0
- package/dist/util/math.js.flow +62 -0
- package/dist/util/mobile-native-utils.d.ts +7 -0
- package/dist/util/mobile-native-utils.js.flow +12 -0
- package/dist/util/react-render.d.ts +2 -0
- package/dist/util/react-render.js.flow +12 -0
- package/dist/util/register-all-widgets-for-testing.d.ts +1 -0
- package/dist/util/register-all-widgets-for-testing.js.flow +7 -0
- package/dist/util/scroll-utils.d.ts +10 -0
- package/dist/util/scroll-utils.js.flow +19 -0
- package/dist/util/sizing-utils.d.ts +9 -0
- package/dist/util/sizing-utils.js.flow +20 -0
- package/dist/util/tex.d.ts +6 -0
- package/dist/util/tex.js.flow +17 -0
- package/dist/util/url-utils.d.ts +6 -0
- package/dist/util/url-utils.js.flow +11 -0
- package/dist/util.d.ts +247 -0
- package/dist/util.js.flow +320 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.js.flow +17 -0
- package/dist/widget-container.d.ts +27 -0
- package/dist/widget-container.js.flow +33 -0
- package/dist/widgets/__tests__/renderQuestion.d.ts +13 -0
- package/dist/widgets/__tests__/renderQuestion.js.flow +28 -0
- package/dist/widgets/categorizer.d.ts +55 -0
- package/dist/widgets/categorizer.js.flow +70 -0
- package/dist/widgets/cs-program.d.ts +48 -0
- package/dist/widgets/cs-program.js.flow +54 -0
- package/dist/widgets/definition.d.ts +42 -0
- package/dist/widgets/definition.js.flow +57 -0
- package/dist/widgets/dropdown.d.ts +51 -0
- package/dist/widgets/dropdown.js.flow +63 -0
- package/dist/widgets/explanation.d.ts +49 -0
- package/dist/widgets/explanation.js.flow +58 -0
- package/dist/widgets/expression.d.ts +90 -0
- package/dist/widgets/expression.js.flow +115 -0
- package/dist/widgets/graded-group-answer-bar.d.ts +13 -0
- package/dist/widgets/graded-group-answer-bar.js.flow +24 -0
- package/dist/widgets/graded-group-set.d.ts +52 -0
- package/dist/widgets/graded-group-set.js.flow +65 -0
- package/dist/widgets/graded-group.d.ts +66 -0
- package/dist/widgets/graded-group.js.flow +78 -0
- package/dist/widgets/grapher/util.d.ts +19 -0
- package/dist/widgets/grapher/util.js.flow +38 -0
- package/dist/widgets/grapher.d.ts +63 -0
- package/dist/widgets/grapher.js.flow +93 -0
- package/dist/widgets/group.d.ts +51 -0
- package/dist/widgets/group.js.flow +60 -0
- package/dist/widgets/iframe.d.ts +52 -0
- package/dist/widgets/iframe.js.flow +53 -0
- package/dist/widgets/image.d.ts +59 -0
- package/dist/widgets/image.js.flow +73 -0
- package/dist/widgets/input-number.d.ts +75 -0
- package/dist/widgets/input-number.js.flow +94 -0
- package/dist/widgets/interaction/arrow-picker.d.ts +12 -0
- package/dist/widgets/interaction/arrow-picker.js.flow +21 -0
- package/dist/widgets/interaction/color-picker.d.ts +14 -0
- package/dist/widgets/interaction/color-picker.js.flow +23 -0
- package/dist/widgets/interaction/dash-picker.d.ts +12 -0
- package/dist/widgets/interaction/dash-picker.js.flow +21 -0
- package/dist/widgets/interaction/element-container.d.ts +21 -0
- package/dist/widgets/interaction/element-container.js.flow +33 -0
- package/dist/widgets/interaction.d.ts +51 -0
- package/dist/widgets/interaction.js.flow +60 -0
- package/dist/widgets/interactive-graph.d.ts +180 -0
- package/dist/widgets/interactive-graph.js.flow +242 -0
- package/dist/widgets/label-image/answer-choices.d.ts +51 -0
- package/dist/widgets/label-image/answer-choices.js.flow +55 -0
- package/dist/widgets/label-image/marker.d.ts +28 -0
- package/dist/widgets/label-image/marker.js.flow +33 -0
- package/dist/widgets/label-image/types.d.ts +14 -0
- package/dist/widgets/label-image/types.js.flow +23 -0
- package/dist/widgets/label-image.d.ts +113 -0
- package/dist/widgets/label-image.js.flow +142 -0
- package/dist/widgets/lights-puzzle.d.ts +47 -0
- package/dist/widgets/lights-puzzle.js.flow +58 -0
- package/dist/widgets/matcher.d.ts +82 -0
- package/dist/widgets/matcher.js.flow +101 -0
- package/dist/widgets/matrix.d.ts +65 -0
- package/dist/widgets/matrix.js.flow +80 -0
- package/dist/widgets/measurer.d.ts +21 -0
- package/dist/widgets/measurer.js.flow +30 -0
- package/dist/widgets/molecule/molecule-drawing.d.ts +5 -0
- package/dist/widgets/molecule/molecule-drawing.js.flow +11 -0
- package/dist/widgets/molecule/molecule-layout.d.ts +82 -0
- package/dist/widgets/molecule/molecule-layout.js.flow +96 -0
- package/dist/widgets/molecule/smiles-parser.d.ts +19 -0
- package/dist/widgets/molecule/smiles-parser.js.flow +26 -0
- package/dist/widgets/molecule.d.ts +51 -0
- package/dist/widgets/molecule.js.flow +60 -0
- package/dist/widgets/number-line.d.ts +90 -0
- package/dist/widgets/number-line.js.flow +102 -0
- package/dist/widgets/numeric-input.d.ts +80 -0
- package/dist/widgets/numeric-input.js.flow +109 -0
- package/dist/widgets/orderer.d.ts +69 -0
- package/dist/widgets/orderer.js.flow +81 -0
- package/dist/widgets/passage/passage-markdown.d.ts +22 -0
- package/dist/widgets/passage/passage-markdown.js.flow +33 -0
- package/dist/widgets/passage-ref-target.d.ts +41 -0
- package/dist/widgets/passage-ref-target.js.flow +52 -0
- package/dist/widgets/passage-ref.d.ts +65 -0
- package/dist/widgets/passage-ref.js.flow +85 -0
- package/dist/widgets/passage.d.ts +125 -0
- package/dist/widgets/passage.js.flow +157 -0
- package/dist/widgets/plotter.d.ts +68 -0
- package/dist/widgets/plotter.js.flow +78 -0
- package/dist/widgets/radio/base-radio.d.ts +38 -0
- package/dist/widgets/radio/base-radio.js.flow +44 -0
- package/dist/widgets/radio/choice-a11y-text.d.ts +2 -0
- package/dist/widgets/radio/choice-a11y-text.js.flow +14 -0
- package/dist/widgets/radio/choice-icon/choice-icon.d.ts +16 -0
- package/dist/widgets/radio/choice-icon/choice-icon.js.flow +22 -0
- package/dist/widgets/radio/choice-icon/cross-out-line.d.ts +9 -0
- package/dist/widgets/radio/choice-icon/cross-out-line.js.flow +16 -0
- package/dist/widgets/radio/choice-icon/shared-styles.d.ts +5 -0
- package/dist/widgets/radio/choice-icon/shared-styles.js.flow +11 -0
- package/dist/widgets/radio/choice-none-above.d.ts +6 -0
- package/dist/widgets/radio/choice-none-above.js.flow +16 -0
- package/dist/widgets/radio/choice.d.ts +23 -0
- package/dist/widgets/radio/choice.js.flow +32 -0
- package/dist/widgets/radio/choice.jsx.fixture.d.ts +1 -0
- package/dist/widgets/radio/choice.jsx.fixture.js.flow +7 -0
- package/dist/widgets/radio/focus-ring.d.ts +12 -0
- package/dist/widgets/radio/focus-ring.js.flow +15 -0
- package/dist/widgets/radio/option-status.d.ts +17 -0
- package/dist/widgets/radio/option-status.js.flow +16 -0
- package/dist/widgets/radio/util.d.ts +6 -0
- package/dist/widgets/radio/util.js.flow +12 -0
- package/dist/widgets/radio/widget.d.ts +62 -0
- package/dist/widgets/radio/widget.js.flow +80 -0
- package/dist/widgets/radio.d.ts +22 -0
- package/dist/widgets/radio.js.flow +31 -0
- package/dist/widgets/reaction-diagram.d.ts +37 -0
- package/dist/widgets/reaction-diagram.js.flow +46 -0
- package/dist/widgets/sequence.d.ts +79 -0
- package/dist/widgets/sequence.js.flow +100 -0
- package/dist/widgets/simulator.d.ts +89 -0
- package/dist/widgets/simulator.js.flow +106 -0
- package/dist/widgets/sorter.d.ts +82 -0
- package/dist/widgets/sorter.js.flow +101 -0
- package/dist/widgets/table.d.ts +56 -0
- package/dist/widgets/table.js.flow +67 -0
- package/dist/widgets/transformer.d.ts +94 -0
- package/dist/widgets/transformer.js.flow +127 -0
- package/dist/widgets/unit.d.ts +77 -0
- package/dist/widgets/unit.js.flow +93 -0
- package/dist/widgets/video-transcript-link.d.ts +12 -0
- package/dist/widgets/video-transcript-link.js.flow +15 -0
- package/dist/widgets/video.d.ts +48 -0
- package/dist/widgets/video.js.flow +57 -0
- package/dist/widgets.d.ts +73 -0
- package/dist/widgets.js.flow +108 -0
- package/dist/zoom.d.ts +1 -0
- package/dist/zoom.js.flow +7 -0
- package/package.json +8 -8
- package/src/__stories__/hints-renderer.stories.tsx +58 -0
- package/src/__stories__/item-renderer.stories.tsx +29 -0
- package/src/__stories__/server-item-renderer.stories.tsx +29 -0
- package/src/__testdata__/graphie.testdata.ts +51 -0
- package/src/__testdata__/item-renderer.testdata.ts +160 -0
- package/src/__testdata__/renderer.testdata.ts +122 -0
- package/src/__tests__/a11y.test.ts +303 -0
- package/src/__tests__/item-renderer.test.tsx +891 -0
- package/src/__tests__/jipt-paragraphs.test.ts +32 -0
- package/src/__tests__/mock-asset-loading-widget.tsx +56 -0
- package/src/__tests__/mock-widget.tsx +33 -0
- package/src/__tests__/perseus-markdown.test.ts +764 -0
- package/src/__tests__/renderability.test.ts +480 -0
- package/src/__tests__/renderer-api.test.tsx +328 -0
- package/src/__tests__/renderer.test.tsx +1802 -0
- package/src/__tests__/server-item-renderer.test.tsx +518 -0
- package/src/__tests__/test-items/image-item.ts +24 -0
- package/src/__tests__/test-items/input-number-1-item.ts +26 -0
- package/src/__tests__/test-items/input-number-2-item.ts +38 -0
- package/src/__tests__/test-items/table-item.ts +29 -0
- package/src/__tests__/tex-wrangler.test.ts +82 -0
- package/src/__tests__/util.test.ts +40 -0
- package/src/a11y.ts +45 -0
- package/src/all-widgets.ts +9 -0
- package/src/article-renderer.tsx +215 -0
- package/src/asset-context.ts +30 -0
- package/src/basic-widgets.ts +16 -0
- package/src/components/__stories__/button-group.stories.tsx +59 -0
- package/src/components/__stories__/fixed-to-responsive.stories.tsx +143 -0
- package/src/components/__stories__/graph.stories.tsx +30 -0
- package/src/components/__stories__/graphie.stories.tsx +35 -0
- package/src/components/__stories__/hud.stories.tsx +39 -0
- package/src/components/__stories__/icon.stories.tsx +63 -0
- package/src/components/__stories__/image-loader.stories.tsx +62 -0
- package/src/components/__stories__/info-tip.stories.tsx +35 -0
- package/src/components/__stories__/inline-icon.stories.tsx +44 -0
- package/src/components/__stories__/input-with-examples.stories.tsx +57 -0
- package/src/components/__stories__/lint.stories.tsx +51 -0
- package/src/components/__stories__/math-input.stories.tsx +34 -0
- package/src/components/__stories__/math-output.stories.tsx +25 -0
- package/src/components/__stories__/multi-button-group.stories.tsx +70 -0
- package/src/components/__stories__/number-input.stories.tsx +43 -0
- package/src/components/__stories__/prop-check-box.stories.tsx +65 -0
- package/src/components/__stories__/range-input.stories.tsx +31 -0
- package/src/components/__stories__/simple-keypad-input.stories.tsx +29 -0
- package/src/components/__stories__/sortable.stories.tsx +77 -0
- package/src/components/__stories__/stub-tag-editor.stories.tsx +49 -0
- package/src/components/__stories__/svg-image.stories.tsx +82 -0
- package/src/components/__stories__/tex.stories.tsx +25 -0
- package/src/components/__stories__/text-buttons.stories.tsx +66 -0
- package/src/components/__stories__/text-input.stories.tsx +39 -0
- package/src/components/__stories__/text-list-editor.stories.tsx +34 -0
- package/src/components/__stories__/zoomable-tex.stories.tsx +39 -0
- package/src/components/__stories__/zoomable.stories.tsx +43 -0
- package/src/components/__testdata__/sorter.testdata.ts +19 -0
- package/src/components/__tests__/number-input.test.tsx +128 -0
- package/src/components/__tests__/sortable.test.tsx +234 -0
- package/src/components/__tests__/sorter.test.tsx +111 -0
- package/src/components/__tests__/zoomable.test.tsx +399 -0
- package/src/components/button-group.tsx +130 -0
- package/src/components/constants.ts +14 -0
- package/src/components/fixed-to-responsive.tsx +165 -0
- package/src/components/graph.tsx +421 -0
- package/src/components/graphie-classes.ts +153 -0
- package/src/components/graphie-movables.ts +338 -0
- package/src/components/graphie.tsx +357 -0
- package/src/components/highlighting/highlightable-content.tsx +243 -0
- package/src/components/highlighting/highlights.ts +207 -0
- package/src/components/highlighting/ranges.ts +304 -0
- package/src/components/highlighting/types.ts +73 -0
- package/src/components/highlighting/ui/highlight-renderer.tsx +175 -0
- package/src/components/highlighting/ui/highlight-set-renderer.tsx +188 -0
- package/src/components/highlighting/ui/highlight-tooltip.tsx +141 -0
- package/src/components/highlighting/ui/highlighting-ui.tsx +153 -0
- package/src/components/highlighting/ui/selection-tracker.tsx +189 -0
- package/src/components/highlighting/ui/types.ts +21 -0
- package/src/components/highlighting/ui/util.ts +165 -0
- package/src/components/highlighting/word-indexer.tsx +156 -0
- package/src/components/hud.tsx +178 -0
- package/src/components/icon.tsx +140 -0
- package/src/components/image-loader.tsx +176 -0
- package/src/components/info-tip/info-tip.tsx +144 -0
- package/src/components/info-tip.tsx +40 -0
- package/src/components/inline-icon.tsx +77 -0
- package/src/components/input-with-examples.tsx +249 -0
- package/src/components/lint.tsx +453 -0
- package/src/components/math-input.tsx +299 -0
- package/src/components/math-output.tsx +128 -0
- package/src/components/multi-button-group.tsx +135 -0
- package/src/components/number-input.tsx +250 -0
- package/src/components/prop-check-box.tsx +80 -0
- package/src/components/range-input.tsx +60 -0
- package/src/components/simple-keypad-input.tsx +88 -0
- package/src/components/sortable.tsx +914 -0
- package/src/components/stub-tag-editor.tsx +53 -0
- package/src/components/svg-image.tsx +777 -0
- package/src/components/tex-buttons.tsx +321 -0
- package/src/components/tex.tsx +67 -0
- package/src/components/text-input.tsx +128 -0
- package/src/components/text-list-editor.tsx +169 -0
- package/src/components/tooltip.tsx +392 -0
- package/src/components/visibility-observer/__stories__/visibility-observer.stories.tsx +143 -0
- package/src/components/visibility-observer/create-visibility-observer.ts +108 -0
- package/src/components/visibility-observer/get-scroll-parent.ts +46 -0
- package/src/components/visibility-observer/visibility-observer.tsx +124 -0
- package/src/components/zoomable-tex.tsx +78 -0
- package/src/components/zoomable.tsx +300 -0
- package/src/components.ts +16 -0
- package/src/definition-context.tsx +60 -0
- package/src/dependencies.ts +21 -0
- package/src/error-boundary.tsx +50 -0
- package/src/extra-widgets.ts +75 -0
- package/src/hint-renderer.tsx +149 -0
- package/src/hints-renderer.tsx +377 -0
- package/src/icon-paths.ts +119 -0
- package/src/index.ts +158 -0
- package/src/init.ts +54 -0
- package/src/interaction-tracker.ts +61 -0
- package/src/interactive2/__tests__/movable-mock.ts +43 -0
- package/src/interactive2/__tests__/movable-point.test.ts +145 -0
- package/src/interactive2/interactive-util.ts +155 -0
- package/src/interactive2/movable-helper-methods.ts +161 -0
- package/src/interactive2/movable-line-options.ts +419 -0
- package/src/interactive2/movable-line.ts +318 -0
- package/src/interactive2/movable-point-options.ts +212 -0
- package/src/interactive2/movable-point.tsx +593 -0
- package/src/interactive2/movable-polygon-options.ts +390 -0
- package/src/interactive2/movable-polygon.ts +368 -0
- package/src/interactive2/movable.ts +275 -0
- package/src/interactive2/objective_.ts +79 -0
- package/src/interactive2/types.ts +2 -0
- package/src/interactive2/wrapped-defaults.ts +87 -0
- package/src/interactive2/wrapped-ellipse.ts +94 -0
- package/src/interactive2/wrapped-line.ts +116 -0
- package/src/interactive2/wrapped-path.ts +51 -0
- package/src/interactive2.ts +73 -0
- package/src/item-renderer.tsx +514 -0
- package/src/item-version.ts +10 -0
- package/src/jipt-paragraphs.ts +49 -0
- package/src/jquery.mobile.vmouse.js +0 -1
- package/src/keypad-context.ts +34 -0
- package/src/loading-context.ts +21 -0
- package/src/logging/log.test.ts +39 -0
- package/src/logging/log.ts +97 -0
- package/src/mixins/__tests__/widget-prop-denylist.test.ts +21 -0
- package/src/mixins/changeable.ts +133 -0
- package/src/mixins/editor-jsonify.ts +13 -0
- package/src/mixins/provide-keypad.tsx +144 -0
- package/src/mixins/widget-jsonify-deprecated.ts +23 -0
- package/src/mixins/widget-prop-denylist.ts +48 -0
- package/src/multi-items/__stories__/multi-renderer.stories.tsx +87 -0
- package/src/multi-items/__testdata__/multi-renderer.testdata.ts +172 -0
- package/src/multi-items/__tests__/items.test.ts +250 -0
- package/src/multi-items/__tests__/multi-renderer.test.tsx +910 -0
- package/src/multi-items/__tests__/prop-type-builders.test.ts +133 -0
- package/src/multi-items/__tests__/shapes.test.ts +43 -0
- package/src/multi-items/__tests__/trees.test.ts +325 -0
- package/src/multi-items/item-types.js.flow +55 -0
- package/src/multi-items/item-types.ts +56 -0
- package/src/multi-items/items.ts +176 -0
- package/src/multi-items/multi-renderer.tsx +564 -0
- package/src/multi-items/prop-type-builders.ts +106 -0
- package/src/multi-items/shape-types.ts +52 -0
- package/src/multi-items/shapes.ts +52 -0
- package/src/multi-items/tree-types.ts +47 -0
- package/src/multi-items/trees.ts +360 -0
- package/src/multi-items.ts +75 -0
- package/src/perseus-api.tsx +188 -0
- package/src/perseus-env.ts +27 -0
- package/src/perseus-error.ts +17 -0
- package/src/perseus-markdown.tsx +407 -0
- package/src/perseus-types.ts +1474 -0
- package/src/question-paragraph.tsx +29 -0
- package/src/renderability.ts +109 -0
- package/src/renderer.tsx +1992 -0
- package/src/server-item-renderer.tsx +465 -0
- package/src/sigfigs.ts +490 -0
- package/src/styles/constants.ts +94 -0
- package/src/styles/global-constants.ts +343 -0
- package/src/styles/global-styles.ts +306 -0
- package/src/styles/media-queries.ts +47 -0
- package/src/styles/shared.ts +130 -0
- package/src/tex-wrangler.ts +182 -0
- package/src/translation-linter.ts +126 -0
- package/src/traversal.ts +138 -0
- package/src/types.ts +468 -0
- package/src/util/a11y.ts +23 -0
- package/src/util/answer-types.test.ts +114 -0
- package/src/util/answer-types.ts +876 -0
- package/src/util/colors.ts +93 -0
- package/src/util/fix-passage-refs.ts +121 -0
- package/src/util/graph-utils.ts +15 -0
- package/src/util/graphie.ts +1617 -0
- package/src/util/interactive.ts +4191 -0
- package/src/util/katex-preprocess.ts +14 -0
- package/src/util/math.ts +289 -0
- package/src/util/mobile-native-utils.test.ts +22 -0
- package/src/util/mobile-native-utils.ts +12 -0
- package/src/util/react-render.tsx +27 -0
- package/src/util/register-all-widgets-for-testing.ts +11 -0
- package/src/util/scroll-utils.ts +155 -0
- package/src/util/sizing-utils.ts +46 -0
- package/src/util/tex.ts +208 -0
- package/src/util/url-utils.ts +34 -0
- package/src/util.ts +1003 -0
- package/src/version.ts +11 -0
- package/src/widget-container.tsx +175 -0
- package/src/widgets/__stories__/categorizer.stories.tsx +14 -0
- package/src/widgets/__stories__/cs-program.stories.tsx +14 -0
- package/src/widgets/__stories__/definition.stories.tsx +111 -0
- package/src/widgets/__stories__/dropdown.stories.tsx +14 -0
- package/src/widgets/__stories__/explanation.stories.tsx +18 -0
- package/src/widgets/__stories__/expression.stories.tsx +29 -0
- package/src/widgets/__stories__/graded-group-set.stories.tsx +31 -0
- package/src/widgets/__stories__/graded-group.stories.tsx +31 -0
- package/src/widgets/__stories__/grapher.stories.tsx +60 -0
- package/src/widgets/__stories__/group.stories.tsx +14 -0
- package/src/widgets/__stories__/iframe.stories.tsx +14 -0
- package/src/widgets/__stories__/image.stories.tsx +61 -0
- package/src/widgets/__stories__/input-number.stories.tsx +110 -0
- package/src/widgets/__stories__/interaction.stories.tsx +14 -0
- package/src/widgets/__stories__/interactive-graph.stories.tsx +60 -0
- package/src/widgets/__stories__/matcher.stories.tsx +14 -0
- package/src/widgets/__stories__/matrix.stories.tsx +14 -0
- package/src/widgets/__stories__/number-line.stories.tsx +14 -0
- package/src/widgets/__stories__/numeric-input.stories.tsx +107 -0
- package/src/widgets/__stories__/on-screen-keypad.stories.tsx +120 -0
- package/src/widgets/__stories__/orderer.stories.tsx +14 -0
- package/src/widgets/__stories__/passage-ref.stories.tsx +22 -0
- package/src/widgets/__stories__/passage.stories.tsx +32 -0
- package/src/widgets/__stories__/radio.stories.tsx +88 -0
- package/src/widgets/__stories__/sorter.stories.tsx +14 -0
- package/src/widgets/__stories__/transformer.stories.tsx +14 -0
- package/src/widgets/__stories__/unit.stories.tsx +30 -0
- package/src/widgets/__stories__/video-transcript-link.stories.tsx +27 -0
- package/src/widgets/__stories__/video.stories.tsx +14 -0
- package/src/widgets/__testdata__/base-radio.testdata.ts +21 -0
- package/src/widgets/__testdata__/categorizer.testdata.ts +49 -0
- package/src/widgets/__testdata__/cs-program.testdata.ts +27 -0
- package/src/widgets/__testdata__/dropdown.testdata.ts +33 -0
- package/src/widgets/__testdata__/explanation.testdata.ts +55 -0
- package/src/widgets/__testdata__/expression.testdata.ts +94 -0
- package/src/widgets/__testdata__/graded-group-set.testdata.ts +356 -0
- package/src/widgets/__testdata__/graded-group.testdata.ts +77 -0
- package/src/widgets/__testdata__/grapher.testdata.ts +374 -0
- package/src/widgets/__testdata__/group.testdata.ts +155 -0
- package/src/widgets/__testdata__/iframe.testdata.ts +35 -0
- package/src/widgets/__testdata__/image.testdata.ts +36 -0
- package/src/widgets/__testdata__/input-number.testdata.ts +72 -0
- package/src/widgets/__testdata__/interaction.testdata.ts +262 -0
- package/src/widgets/__testdata__/interactive-graph.testdata.ts +644 -0
- package/src/widgets/__testdata__/matcher.testdata.ts +33 -0
- package/src/widgets/__testdata__/matrix.testdata.ts +28 -0
- package/src/widgets/__testdata__/number-line.testdata.ts +30 -0
- package/src/widgets/__testdata__/numeric-input.testdata.ts +266 -0
- package/src/widgets/__testdata__/orderer.testdata.ts +57 -0
- package/src/widgets/__testdata__/passage-ref.testdata.ts +101 -0
- package/src/widgets/__testdata__/passage.testdata.ts +95 -0
- package/src/widgets/__testdata__/radio.testdata.ts +261 -0
- package/src/widgets/__testdata__/sorter.testdata.ts +19 -0
- package/src/widgets/__testdata__/transformer.testdata.ts +108 -0
- package/src/widgets/__testdata__/video.testdata.ts +21 -0
- package/src/widgets/__tests__/__snapshots__/input-number.test.ts.snap +497 -0
- package/src/widgets/__tests__/__snapshots__/unit.test.tsx.snap +20 -0
- package/src/widgets/__tests__/categorizer.test.ts +203 -0
- package/src/widgets/__tests__/cs-program.test.ts +43 -0
- package/src/widgets/__tests__/definition.test.ts +136 -0
- package/src/widgets/__tests__/dropdown.test.ts +105 -0
- package/src/widgets/__tests__/explanation.test.ts +198 -0
- package/src/widgets/__tests__/expression.test.ts +431 -0
- package/src/widgets/__tests__/graded-group-set-jipt.test.ts +30 -0
- package/src/widgets/__tests__/graded-group-set.test.ts +288 -0
- package/src/widgets/__tests__/graded-group.test.ts +232 -0
- package/src/widgets/__tests__/grapher.cypress.ts +576 -0
- package/src/widgets/__tests__/grapher.test.ts +33 -0
- package/src/widgets/__tests__/group.test.tsx +512 -0
- package/src/widgets/__tests__/iframe.test.ts +45 -0
- package/src/widgets/__tests__/image.test.ts +62 -0
- package/src/widgets/__tests__/input-number.test.ts +354 -0
- package/src/widgets/__tests__/interaction.test.ts +31 -0
- package/src/widgets/__tests__/interactive-graph.cypress.ts +815 -0
- package/src/widgets/__tests__/interactive-graph.test.ts +106 -0
- package/src/widgets/__tests__/matcher.test.tsx +152 -0
- package/src/widgets/__tests__/matrix.test.ts +82 -0
- package/src/widgets/__tests__/number-line.test.ts +79 -0
- package/src/widgets/__tests__/numeric-input.test.ts +502 -0
- package/src/widgets/__tests__/orderer.test.ts +73 -0
- package/src/widgets/__tests__/passage-ref.test.ts +168 -0
- package/src/widgets/__tests__/passage.test.tsx +224 -0
- package/src/widgets/__tests__/radio/base-radio.test.tsx +470 -0
- package/src/widgets/__tests__/radio/choice-icon.test.tsx +90 -0
- package/src/widgets/__tests__/radio/choice.test.tsx +310 -0
- package/src/widgets/__tests__/radio/focus-ring.test.tsx +23 -0
- package/src/widgets/__tests__/radio/option-status.test.tsx +77 -0
- package/src/widgets/__tests__/radio.test.ts +807 -0
- package/src/widgets/__tests__/renderQuestion.tsx +77 -0
- package/src/widgets/__tests__/unit.test.tsx +168 -0
- package/src/widgets/__tests__/video.test.ts +53 -0
- package/src/widgets/categorizer.tsx +343 -0
- package/src/widgets/cs-program.tsx +227 -0
- package/src/widgets/definition.tsx +121 -0
- package/src/widgets/dropdown.tsx +142 -0
- package/src/widgets/explanation.tsx +317 -0
- package/src/widgets/expression.tsx +647 -0
- package/src/widgets/graded-group-answer-bar.tsx +142 -0
- package/src/widgets/graded-group-set.tsx +296 -0
- package/src/widgets/graded-group.tsx +466 -0
- package/src/widgets/grapher/util.tsx +803 -0
- package/src/widgets/grapher.tsx +674 -0
- package/src/widgets/group.tsx +196 -0
- package/src/widgets/iframe.tsx +195 -0
- package/src/widgets/image.tsx +309 -0
- package/src/widgets/input-number.tsx +413 -0
- package/src/widgets/interaction/arrow-picker.tsx +38 -0
- package/src/widgets/interaction/color-picker.tsx +69 -0
- package/src/widgets/interaction/dash-picker.tsx +45 -0
- package/src/widgets/interaction/element-container.tsx +101 -0
- package/src/widgets/interaction.tsx +826 -0
- package/src/widgets/interactive-graph.tsx +2921 -0
- package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +98 -0
- package/src/widgets/label-image/__stories__/marker.stories.tsx +181 -0
- package/src/widgets/label-image/answer-choices.tsx +300 -0
- package/src/widgets/label-image/marker.tsx +364 -0
- package/src/widgets/label-image/types.ts +26 -0
- package/src/widgets/label-image.test.ts +840 -0
- package/src/widgets/label-image.tsx +849 -0
- package/src/widgets/lights-puzzle.tsx +359 -0
- package/src/widgets/matcher.tsx +278 -0
- package/src/widgets/matrix.tsx +637 -0
- package/src/widgets/measurer.tsx +217 -0
- package/src/widgets/molecule/__tests__/layout.test.ts +162 -0
- package/src/widgets/molecule/__tests__/parser.test.ts +155 -0
- package/src/widgets/molecule/molecule-drawing.ts +173 -0
- package/src/widgets/molecule/molecule-layout.ts +362 -0
- package/src/widgets/molecule/smiles-parser.ts +296 -0
- package/src/widgets/molecule.tsx +172 -0
- package/src/widgets/number-line.tsx +864 -0
- package/src/widgets/numeric-input.tsx +563 -0
- package/src/widgets/orderer.tsx +788 -0
- package/src/widgets/passage/__tests__/passage-markdown.test.ts +376 -0
- package/src/widgets/passage/passage-markdown.tsx +475 -0
- package/src/widgets/passage-ref-target.tsx +73 -0
- package/src/widgets/passage-ref.tsx +215 -0
- package/src/widgets/passage.tsx +584 -0
- package/src/widgets/plotter.tsx +1204 -0
- package/src/widgets/radio/__stories__/base-radio.stories.tsx +204 -0
- package/src/widgets/radio/__stories__/choice-icon.stories.tsx +168 -0
- package/src/widgets/radio/__stories__/choice-none-above.stories.tsx +40 -0
- package/src/widgets/radio/__stories__/choice.stories.tsx +137 -0
- package/src/widgets/radio/__stories__/focus-ring.stories.tsx +43 -0
- package/src/widgets/radio/__stories__/option-status.stories.tsx +105 -0
- package/src/widgets/radio/base-radio.tsx +509 -0
- package/src/widgets/radio/choice-a11y-text.ts +61 -0
- package/src/widgets/radio/choice-icon/choice-icon.tsx +217 -0
- package/src/widgets/radio/choice-icon/cross-out-line.tsx +53 -0
- package/src/widgets/radio/choice-icon/shared-styles.ts +15 -0
- package/src/widgets/radio/choice-none-above.tsx +56 -0
- package/src/widgets/radio/choice.jsx.fixture.ts +45 -0
- package/src/widgets/radio/choice.tsx +358 -0
- package/src/widgets/radio/focus-ring.tsx +57 -0
- package/src/widgets/radio/option-status.tsx +100 -0
- package/src/widgets/radio/util.test.ts +18 -0
- package/src/widgets/radio/util.ts +31 -0
- package/src/widgets/radio/widget.tsx +521 -0
- package/src/widgets/radio.ts +157 -0
- package/src/widgets/reaction-diagram.tsx +144 -0
- package/src/widgets/sequence.tsx +137 -0
- package/src/widgets/simulator.tsx +831 -0
- package/src/widgets/sorter.tsx +154 -0
- package/src/widgets/table.tsx +393 -0
- package/src/widgets/transformer.tsx +2951 -0
- package/src/widgets/unit.tsx +374 -0
- package/src/widgets/video-transcript-link.test.tsx +213 -0
- package/src/widgets/video-transcript-link.tsx +84 -0
- package/src/widgets/video.tsx +124 -0
- package/src/widgets.ts +444 -0
- package/src/zoom.ts +558 -0
- package/tsconfig.json +25 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/src/__stories__/hints-renderer.stories.jsx +0 -60
- package/src/__stories__/item-renderer.stories.jsx +0 -27
- package/src/__stories__/server-item-renderer.stories.jsx +0 -27
- package/src/__testdata__/graphie_testdata.js +0 -53
- package/src/__testdata__/item-renderer_testdata.js +0 -161
- package/src/__testdata__/renderer_testdata.js +0 -123
- package/src/__tests__/a11y_test.jsx +0 -304
- package/src/__tests__/item-renderer_test.jsx +0 -892
- package/src/__tests__/jipt-paragraphs_test.js +0 -34
- package/src/__tests__/mock-asset-loading-widget.jsx +0 -57
- package/src/__tests__/mock-widget.jsx +0 -35
- package/src/__tests__/perseus-markdown_test.jsx +0 -747
- package/src/__tests__/renderability_test.jsx +0 -481
- package/src/__tests__/renderer-api_test.jsx +0 -309
- package/src/__tests__/renderer_test.jsx +0 -1792
- package/src/__tests__/server-item-renderer_test.jsx +0 -516
- package/src/__tests__/test-items/image-item.js +0 -25
- package/src/__tests__/test-items/input-number-1-item.js +0 -27
- package/src/__tests__/test-items/input-number-2-item.js +0 -39
- package/src/__tests__/test-items/table-item.js +0 -30
- package/src/__tests__/tex-wrangler_test.js +0 -83
- package/src/__tests__/util_test.js +0 -41
- package/src/a11y.js +0 -46
- package/src/all-widgets.js +0 -9
- package/src/article-renderer.jsx +0 -214
- package/src/asset-context.js +0 -30
- package/src/basic-widgets.js +0 -13
- package/src/components/__stories__/button-group.stories.jsx +0 -61
- package/src/components/__stories__/fixed-to-responsive.stories.jsx +0 -132
- package/src/components/__stories__/graph.stories.jsx +0 -27
- package/src/components/__stories__/graphie.stories.jsx +0 -32
- package/src/components/__stories__/hud.stories.jsx +0 -36
- package/src/components/__stories__/icon.stories.jsx +0 -56
- package/src/components/__stories__/image-loader.stories.jsx +0 -60
- package/src/components/__stories__/info-tip.stories.jsx +0 -33
- package/src/components/__stories__/inline-icon.stories.jsx +0 -39
- package/src/components/__stories__/input-with-examples.stories.jsx +0 -52
- package/src/components/__stories__/lint.stories.jsx +0 -40
- package/src/components/__stories__/math-input.stories.jsx +0 -29
- package/src/components/__stories__/math-output.stories.jsx +0 -24
- package/src/components/__stories__/multi-button-group.stories.jsx +0 -69
- package/src/components/__stories__/number-input.stories.jsx +0 -42
- package/src/components/__stories__/prop-check-box.stories.jsx +0 -62
- package/src/components/__stories__/range-input.stories.jsx +0 -28
- package/src/components/__stories__/simple-keypad-input.stories.jsx +0 -28
- package/src/components/__stories__/sortable.stories.jsx +0 -73
- package/src/components/__stories__/stub-tag-editor.stories.jsx +0 -44
- package/src/components/__stories__/svg-image.stories.jsx +0 -71
- package/src/components/__stories__/tex.stories.jsx +0 -24
- package/src/components/__stories__/text-buttons.stories.jsx +0 -51
- package/src/components/__stories__/text-input.stories.jsx +0 -34
- package/src/components/__stories__/text-list-editor.stories.jsx +0 -33
- package/src/components/__stories__/zoomable-tex.stories.jsx +0 -39
- package/src/components/__stories__/zoomable.stories.jsx +0 -40
- package/src/components/__testdata__/sorter_testdata.js +0 -20
- package/src/components/__tests__/number-input_test.jsx +0 -112
- package/src/components/__tests__/sortable_test.jsx +0 -234
- package/src/components/__tests__/sorter_test.jsx +0 -111
- package/src/components/__tests__/zoomable_test.jsx +0 -388
- package/src/components/button-group.jsx +0 -131
- package/src/components/constants.js +0 -15
- package/src/components/fixed-to-responsive.jsx +0 -165
- package/src/components/graph.jsx +0 -413
- package/src/components/graphie-classes.jsx +0 -148
- package/src/components/graphie-movables.jsx +0 -342
- package/src/components/graphie.jsx +0 -353
- package/src/components/highlighting/highlightable-content.jsx +0 -245
- package/src/components/highlighting/highlights.js +0 -208
- package/src/components/highlighting/ranges.js +0 -299
- package/src/components/highlighting/types.js +0 -73
- package/src/components/highlighting/ui/highlight-renderer.jsx +0 -179
- package/src/components/highlighting/ui/highlight-set-renderer.jsx +0 -190
- package/src/components/highlighting/ui/highlight-tooltip.jsx +0 -143
- package/src/components/highlighting/ui/highlighting-ui.jsx +0 -163
- package/src/components/highlighting/ui/selection-tracker.jsx +0 -184
- package/src/components/highlighting/ui/types.js +0 -21
- package/src/components/highlighting/ui/util.js +0 -166
- package/src/components/highlighting/word-indexer.jsx +0 -155
- package/src/components/hud.jsx +0 -179
- package/src/components/icon.jsx +0 -149
- package/src/components/image-loader.jsx +0 -175
- package/src/components/info-tip/info-tip.jsx +0 -146
- package/src/components/info-tip.jsx +0 -38
- package/src/components/inline-icon.jsx +0 -81
- package/src/components/input-with-examples.jsx +0 -248
- package/src/components/lint.jsx +0 -455
- package/src/components/math-input.jsx +0 -293
- package/src/components/math-output.jsx +0 -128
- package/src/components/multi-button-group.jsx +0 -137
- package/src/components/number-input.jsx +0 -253
- package/src/components/prop-check-box.jsx +0 -80
- package/src/components/range-input.jsx +0 -61
- package/src/components/simple-keypad-input.jsx +0 -88
- package/src/components/sortable.jsx +0 -872
- package/src/components/stub-tag-editor.jsx +0 -54
- package/src/components/svg-image.jsx +0 -765
- package/src/components/tex-buttons.jsx +0 -320
- package/src/components/tex.jsx +0 -70
- package/src/components/text-input.jsx +0 -133
- package/src/components/text-list-editor.jsx +0 -157
- package/src/components/tooltip.jsx +0 -392
- package/src/components/visibility-observer/__stories__/visibility-observer.stories.jsx +0 -140
- package/src/components/visibility-observer/get-scroll-parent.js +0 -45
- package/src/components/visibility-observer/visibility-observer.js +0 -109
- package/src/components/visibility-observer/visibility-observer.jsx +0 -123
- package/src/components/zoomable-tex.jsx +0 -70
- package/src/components/zoomable.jsx +0 -294
- package/src/components.js +0 -17
- package/src/definition-context.js +0 -59
- package/src/dependencies.js +0 -22
- package/src/error-boundary.jsx +0 -48
- package/src/extra-widgets.js +0 -75
- package/src/hint-renderer.jsx +0 -155
- package/src/hints-renderer.jsx +0 -367
- package/src/icon-paths.js +0 -120
- package/src/index.js +0 -159
- package/src/init.js +0 -49
- package/src/interaction-tracker.js +0 -60
- package/src/interactive2/__tests__/movable-mock.js +0 -48
- package/src/interactive2/__tests__/movable-point_test.js +0 -146
- package/src/interactive2/interactive-util.js +0 -152
- package/src/interactive2/movable-helper-methods.js +0 -160
- package/src/interactive2/movable-line-options.js +0 -392
- package/src/interactive2/movable-line.js +0 -322
- package/src/interactive2/movable-point-options.js +0 -190
- package/src/interactive2/movable-point.jsx +0 -597
- package/src/interactive2/movable-polygon-options.js +0 -351
- package/src/interactive2/movable-polygon.js +0 -368
- package/src/interactive2/movable.js +0 -273
- package/src/interactive2/objective_.js +0 -79
- package/src/interactive2/types.js +0 -3
- package/src/interactive2/wrapped-defaults.js +0 -86
- package/src/interactive2/wrapped-ellipse.js +0 -86
- package/src/interactive2/wrapped-line.js +0 -109
- package/src/interactive2/wrapped-path.js +0 -47
- package/src/interactive2.js +0 -91
- package/src/item-renderer.jsx +0 -514
- package/src/item-version.js +0 -12
- package/src/jipt-paragraphs.js +0 -52
- package/src/keypad-context.js +0 -33
- package/src/loading-context.js +0 -20
- package/src/logging/log.js +0 -107
- package/src/logging/log_test.js +0 -41
- package/src/mixins/__tests__/widget-prop-denylist_test.js +0 -23
- package/src/mixins/changeable.jsx +0 -115
- package/src/mixins/editor-jsonify.jsx +0 -13
- package/src/mixins/provide-keypad.jsx +0 -131
- package/src/mixins/widget-jsonify-deprecated.jsx +0 -23
- package/src/mixins/widget-prop-denylist.js +0 -49
- package/src/multi-items/__stories__/multi-renderer.stories.js +0 -88
- package/src/multi-items/__testdata__/multi-renderer_testdata.js +0 -174
- package/src/multi-items/__tests__/items_test.js +0 -245
- package/src/multi-items/__tests__/multi-renderer_test.js +0 -903
- package/src/multi-items/__tests__/prop-type-builders_test.js +0 -134
- package/src/multi-items/__tests__/shapes_test.js +0 -44
- package/src/multi-items/__tests__/trees_test.js +0 -316
- package/src/multi-items/items.js +0 -176
- package/src/multi-items/multi-renderer.jsx +0 -543
- package/src/multi-items/prop-type-builders.js +0 -105
- package/src/multi-items/shape-types.js +0 -47
- package/src/multi-items/shapes.js +0 -53
- package/src/multi-items/tree-types.js +0 -42
- package/src/multi-items/trees.js +0 -345
- package/src/multi-items.js +0 -76
- package/src/perseus-api.jsx +0 -221
- package/src/perseus-env.js +0 -26
- package/src/perseus-error.js +0 -19
- package/src/perseus-markdown.jsx +0 -408
- package/src/perseus-types.js +0 -1471
- package/src/question-paragraph.jsx +0 -30
- package/src/renderability.jsx +0 -108
- package/src/renderer.jsx +0 -1937
- package/src/server-item-renderer.jsx +0 -467
- package/src/sigfigs.jsx +0 -492
- package/src/styles/constants.js +0 -96
- package/src/styles/global-constants.js +0 -344
- package/src/styles/global-styles.js +0 -307
- package/src/styles/media-queries.js +0 -48
- package/src/styles/shared.js +0 -132
- package/src/tex-wrangler.js +0 -176
- package/src/translation-linter.js +0 -125
- package/src/traversal.jsx +0 -141
- package/src/types.js +0 -488
- package/src/util/a11y.js +0 -24
- package/src/util/answer-types.js +0 -861
- package/src/util/answer-types_test.js +0 -115
- package/src/util/colors.js +0 -94
- package/src/util/fix-passage-refs.jsx +0 -122
- package/src/util/graph-utils.js +0 -16
- package/src/util/graphie.js +0 -1586
- package/src/util/interactive.js +0 -4078
- package/src/util/katex-preprocess.js +0 -16
- package/src/util/math.js +0 -289
- package/src/util/mobile-native-utils.js +0 -14
- package/src/util/mobile-native-utils_test.js +0 -24
- package/src/util/react-render.js +0 -27
- package/src/util/register-all-widgets-for-testing.js +0 -12
- package/src/util/scroll-utils.js +0 -162
- package/src/util/sizing-utils.js +0 -46
- package/src/util/tex.js +0 -203
- package/src/util/url-utils.js +0 -36
- package/src/util.js +0 -984
- package/src/version.js +0 -11
- package/src/widget-container.jsx +0 -178
- package/src/widgets/__stories__/categorizer.stories.jsx +0 -15
- package/src/widgets/__stories__/cs-program.stories.jsx +0 -15
- package/src/widgets/__stories__/definition.stories.jsx +0 -108
- package/src/widgets/__stories__/dropdown.stories.jsx +0 -15
- package/src/widgets/__stories__/explanation.stories.jsx +0 -19
- package/src/widgets/__stories__/expression.stories.jsx +0 -26
- package/src/widgets/__stories__/graded-group-set.stories.jsx +0 -32
- package/src/widgets/__stories__/graded-group.stories.jsx +0 -32
- package/src/widgets/__stories__/grapher.stories.jsx +0 -47
- package/src/widgets/__stories__/group.stories.jsx +0 -15
- package/src/widgets/__stories__/iframe.stories.jsx +0 -15
- package/src/widgets/__stories__/image.stories.jsx +0 -62
- package/src/widgets/__stories__/input-number.stories.jsx +0 -102
- package/src/widgets/__stories__/interaction.stories.jsx +0 -15
- package/src/widgets/__stories__/interactive-graph.stories.jsx +0 -61
- package/src/widgets/__stories__/matcher.stories.jsx +0 -15
- package/src/widgets/__stories__/matrix.stories.jsx +0 -15
- package/src/widgets/__stories__/number-line.stories.jsx +0 -15
- package/src/widgets/__stories__/numeric-input.stories.jsx +0 -106
- package/src/widgets/__stories__/on-screen-keypad.stories.jsx +0 -118
- package/src/widgets/__stories__/orderer.stories.jsx +0 -15
- package/src/widgets/__stories__/passage-ref.stories.jsx +0 -23
- package/src/widgets/__stories__/passage.stories.jsx +0 -27
- package/src/widgets/__stories__/radio.stories.jsx +0 -90
- package/src/widgets/__stories__/sorter.stories.jsx +0 -15
- package/src/widgets/__stories__/transformer.stories.jsx +0 -15
- package/src/widgets/__stories__/unit.stories.jsx +0 -45
- package/src/widgets/__stories__/video-transcript-link.stories.jsx +0 -24
- package/src/widgets/__stories__/video.stories.jsx +0 -15
- package/src/widgets/__testdata__/base-radio_testdata.js +0 -22
- package/src/widgets/__testdata__/categorizer_testdata.js +0 -50
- package/src/widgets/__testdata__/cs-program_testdata.js +0 -28
- package/src/widgets/__testdata__/dropdown_testdata.js +0 -34
- package/src/widgets/__testdata__/explanation_testdata.js +0 -56
- package/src/widgets/__testdata__/expression_testdata.js +0 -95
- package/src/widgets/__testdata__/graded-group-set_testdata.js +0 -358
- package/src/widgets/__testdata__/graded-group_testdata.js +0 -79
- package/src/widgets/__testdata__/grapher_testdata.js +0 -376
- package/src/widgets/__testdata__/group_testdata.js +0 -156
- package/src/widgets/__testdata__/iframe_testdata.js +0 -36
- package/src/widgets/__testdata__/image_testdata.js +0 -38
- package/src/widgets/__testdata__/input-number_testdata.js +0 -73
- package/src/widgets/__testdata__/interaction_testdata.js +0 -264
- package/src/widgets/__testdata__/interactive-graph_testdata.js +0 -646
- package/src/widgets/__testdata__/matcher_testdata.js +0 -34
- package/src/widgets/__testdata__/matrix_testdata.js +0 -29
- package/src/widgets/__testdata__/number-line_testdata.js +0 -31
- package/src/widgets/__testdata__/numeric-input_testdata.js +0 -266
- package/src/widgets/__testdata__/orderer_testdata.js +0 -58
- package/src/widgets/__testdata__/passage-ref_testdata.js +0 -103
- package/src/widgets/__testdata__/passage_testdata.js +0 -96
- package/src/widgets/__testdata__/radio_testdata.js +0 -262
- package/src/widgets/__testdata__/sorter_testdata.js +0 -20
- package/src/widgets/__testdata__/transformer_testdata.js +0 -109
- package/src/widgets/__testdata__/video_testdata.js +0 -22
- package/src/widgets/__tests__/__snapshots__/expression_test.jsx.snap +0 -38
- package/src/widgets/__tests__/__snapshots__/input-number_test.jsx.snap +0 -978
- package/src/widgets/__tests__/categorizer_test.jsx +0 -205
- package/src/widgets/__tests__/cs-program_test.jsx +0 -45
- package/src/widgets/__tests__/definition_test.js +0 -137
- package/src/widgets/__tests__/dropdown_test.jsx +0 -106
- package/src/widgets/__tests__/explanation_test.js +0 -199
- package/src/widgets/__tests__/expression_test.jsx +0 -439
- package/src/widgets/__tests__/graded-group-set-jipt_test.jsx +0 -32
- package/src/widgets/__tests__/graded-group-set_test.jsx +0 -290
- package/src/widgets/__tests__/graded-group_test.jsx +0 -233
- package/src/widgets/__tests__/grapher.cypress.jsx +0 -546
- package/src/widgets/__tests__/grapher_test.jsx +0 -35
- package/src/widgets/__tests__/group_test.jsx +0 -512
- package/src/widgets/__tests__/iframe_test.jsx +0 -47
- package/src/widgets/__tests__/image_test.jsx +0 -65
- package/src/widgets/__tests__/input-number_test.jsx +0 -360
- package/src/widgets/__tests__/interaction_test.jsx +0 -33
- package/src/widgets/__tests__/interactive-graph.cypress.jsx +0 -777
- package/src/widgets/__tests__/interactive-graph_test.jsx +0 -105
- package/src/widgets/__tests__/matcher_test.jsx +0 -157
- package/src/widgets/__tests__/matrix_test.jsx +0 -84
- package/src/widgets/__tests__/number-line_test.jsx +0 -81
- package/src/widgets/__tests__/numeric-input_test.jsx +0 -514
- package/src/widgets/__tests__/orderer_test.jsx +0 -75
- package/src/widgets/__tests__/passage-ref_test.jsx +0 -166
- package/src/widgets/__tests__/passage_test.jsx +0 -212
- package/src/widgets/__tests__/radio/base-radio_test.jsx +0 -443
- package/src/widgets/__tests__/radio/choice-icon_test.jsx +0 -91
- package/src/widgets/__tests__/radio/choice_test.jsx +0 -311
- package/src/widgets/__tests__/radio/focus-ring_test.jsx +0 -23
- package/src/widgets/__tests__/radio/option-status_test.jsx +0 -65
- package/src/widgets/__tests__/radio_test.jsx +0 -810
- package/src/widgets/__tests__/renderQuestion.jsx +0 -66
- package/src/widgets/__tests__/unit_test.jsx +0 -144
- package/src/widgets/__tests__/video_test.jsx +0 -55
- package/src/widgets/categorizer.jsx +0 -343
- package/src/widgets/cs-program.jsx +0 -227
- package/src/widgets/definition.jsx +0 -121
- package/src/widgets/dropdown.jsx +0 -141
- package/src/widgets/explanation.jsx +0 -315
- package/src/widgets/expression.jsx +0 -652
- package/src/widgets/graded-group-answer-bar.jsx +0 -152
- package/src/widgets/graded-group-set.jsx +0 -296
- package/src/widgets/graded-group.jsx +0 -453
- package/src/widgets/grapher/util.jsx +0 -806
- package/src/widgets/grapher.jsx +0 -663
- package/src/widgets/group.jsx +0 -195
- package/src/widgets/iframe.jsx +0 -198
- package/src/widgets/image.jsx +0 -310
- package/src/widgets/input-number.jsx +0 -412
- package/src/widgets/interaction/arrow-picker.jsx +0 -39
- package/src/widgets/interaction/color-picker.jsx +0 -69
- package/src/widgets/interaction/dash-picker.jsx +0 -46
- package/src/widgets/interaction/element-container.jsx +0 -97
- package/src/widgets/interaction.jsx +0 -742
- package/src/widgets/interactive-graph.jsx +0 -2820
- package/src/widgets/label-image/__stories__/answer-choices.stories.jsx +0 -93
- package/src/widgets/label-image/__stories__/marker.stories.jsx +0 -172
- package/src/widgets/label-image/answer-choices.jsx +0 -311
- package/src/widgets/label-image/marker.jsx +0 -365
- package/src/widgets/label-image/types.js +0 -33
- package/src/widgets/label-image.jsx +0 -841
- package/src/widgets/label-image_test.js +0 -840
- package/src/widgets/lights-puzzle.jsx +0 -356
- package/src/widgets/matcher.jsx +0 -274
- package/src/widgets/matrix.jsx +0 -632
- package/src/widgets/measurer.jsx +0 -215
- package/src/widgets/molecule/__tests__/layout_test.jsx +0 -159
- package/src/widgets/molecule/__tests__/parser_test.jsx +0 -142
- package/src/widgets/molecule/molecule-drawing.jsx +0 -175
- package/src/widgets/molecule/molecule-layout.jsx +0 -367
- package/src/widgets/molecule/smiles-parser.jsx +0 -300
- package/src/widgets/molecule.jsx +0 -172
- package/src/widgets/number-line.jsx +0 -858
- package/src/widgets/numeric-input.jsx +0 -553
- package/src/widgets/orderer.jsx +0 -756
- package/src/widgets/passage/__tests__/passage-markdown_test.jsx +0 -373
- package/src/widgets/passage/passage-markdown.jsx +0 -472
- package/src/widgets/passage-ref-target.jsx +0 -73
- package/src/widgets/passage-ref.jsx +0 -214
- package/src/widgets/passage.jsx +0 -576
- package/src/widgets/plotter.jsx +0 -1198
- package/src/widgets/radio/__stories__/base-radio.stories.jsx +0 -189
- package/src/widgets/radio/__stories__/choice-icon.stories.jsx +0 -166
- package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +0 -41
- package/src/widgets/radio/__stories__/choice.stories.jsx +0 -138
- package/src/widgets/radio/__stories__/focus-ring.stories.jsx +0 -44
- package/src/widgets/radio/__stories__/option-status.stories.jsx +0 -104
- package/src/widgets/radio/base-radio.jsx +0 -494
- package/src/widgets/radio/choice-a11y-text.js +0 -62
- package/src/widgets/radio/choice-icon/choice-icon.jsx +0 -214
- package/src/widgets/radio/choice-icon/cross-out-line.jsx +0 -52
- package/src/widgets/radio/choice-icon/shared-styles.js +0 -16
- package/src/widgets/radio/choice-none-above.jsx +0 -63
- package/src/widgets/radio/choice.jsx +0 -382
- package/src/widgets/radio/choice.jsx.fixture.js +0 -46
- package/src/widgets/radio/focus-ring.jsx +0 -58
- package/src/widgets/radio/option-status.jsx +0 -98
- package/src/widgets/radio/util.js +0 -32
- package/src/widgets/radio/util_test.js +0 -19
- package/src/widgets/radio/widget.jsx +0 -515
- package/src/widgets/radio.jsx +0 -155
- package/src/widgets/reaction-diagram.jsx +0 -137
- package/src/widgets/sequence.jsx +0 -143
- package/src/widgets/simulator.jsx +0 -806
- package/src/widgets/sorter.jsx +0 -149
- package/src/widgets/table.jsx +0 -397
- package/src/widgets/transformer.jsx +0 -2876
- package/src/widgets/unit.jsx +0 -372
- package/src/widgets/video-transcript-link.jsx +0 -84
- package/src/widgets/video-transcript-link_test.js +0 -211
- package/src/widgets/video.jsx +0 -126
- package/src/widgets.js +0 -431
- package/src/zoom.js +0 -539
- /package/{src/multi-items/item-types.js → dist/multi-items/item-types.js.flow} +0 -0
- /package/dist/{strings.js → strings} +0 -0
- /package/src/__tests__/__snapshots__/{item-renderer_test.jsx.snap → item-renderer.test.tsx.snap} +0 -0
- /package/src/__tests__/__snapshots__/{perseus-markdown_test.jsx.snap → perseus-markdown.test.ts.snap} +0 -0
- /package/src/__tests__/__snapshots__/{renderer_test.jsx.snap → renderer.test.tsx.snap} +0 -0
- /package/src/__tests__/__snapshots__/{server-item-renderer_test.jsx.snap → server-item-renderer.test.tsx.snap} +0 -0
- /package/src/components/__tests__/__snapshots__/{sortable_test.jsx.snap → sortable.test.tsx.snap} +0 -0
- /package/src/components/__tests__/__snapshots__/{sorter_test.jsx.snap → sorter.test.tsx.snap} +0 -0
- /package/src/multi-items/__tests__/__snapshots__/{multi-renderer_test.js.snap → multi-renderer.test.tsx.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{categorizer_test.jsx.snap → categorizer.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{cs-program_test.jsx.snap → cs-program.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{definition_test.js.snap → definition.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{dropdown_test.jsx.snap → dropdown.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{explanation_test.js.snap → explanation.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{graded-group-set-jipt_test.jsx.snap → graded-group-set-jipt.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{graded-group-set_test.jsx.snap → graded-group-set.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{graded-group_test.jsx.snap → graded-group.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{grapher_test.jsx.snap → grapher.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{group_test.jsx.snap → group.test.tsx.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{iframe_test.jsx.snap → iframe.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{image_test.jsx.snap → image.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{interaction_test.jsx.snap → interaction.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{interactive-graph_test.jsx.snap → interactive-graph.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{matcher_test.jsx.snap → matcher.test.tsx.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{matrix_test.jsx.snap → matrix.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{number-line_test.jsx.snap → number-line.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{numeric-input_test.jsx.snap → numeric-input.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{orderer_test.jsx.snap → orderer.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{passage-ref_test.jsx.snap → passage-ref.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{passage_test.jsx.snap → passage.test.tsx.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{radio_test.jsx.snap → radio.test.ts.snap} +0 -0
- /package/src/widgets/__tests__/__snapshots__/{video_test.jsx.snap → video.test.ts.snap} +0 -0
|
@@ -0,0 +1,4191 @@
|
|
|
1
|
+
/* eslint-disable @babel/no-invalid-this */
|
|
2
|
+
/**
|
|
3
|
+
* Interactive graphie utilities.
|
|
4
|
+
*
|
|
5
|
+
* This file exposes a couple functions, but mostly it adds functions to the
|
|
6
|
+
* `Graphie` prototype for dealing with interactive graphie elements.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// TODO(emily): This file breaks our line length limits like nobody's business.
|
|
10
|
+
// Figure out how to fix that.
|
|
11
|
+
import {
|
|
12
|
+
vector as kvector,
|
|
13
|
+
point as kpoint,
|
|
14
|
+
line as kline,
|
|
15
|
+
} from "@khanacademy/kmath";
|
|
16
|
+
import $ from "jquery";
|
|
17
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
18
|
+
import Raphael from "raphael";
|
|
19
|
+
import _ from "underscore";
|
|
20
|
+
|
|
21
|
+
// NOTE(jeresig): We include a special copy of jQuery vmouse so
|
|
22
|
+
// that Perseus will continue to work in mobile native devices
|
|
23
|
+
// (this should have no impact in the browser)
|
|
24
|
+
// eslint-disable-next-line import/no-unassigned-import
|
|
25
|
+
import "../jquery.mobile.vmouse";
|
|
26
|
+
import InteractiveUtil from "../interactive2/interactive-util";
|
|
27
|
+
import WrappedEllipse from "../interactive2/wrapped-ellipse";
|
|
28
|
+
import WrappedLine from "../interactive2/wrapped-line";
|
|
29
|
+
import WrappedPath from "../interactive2/wrapped-path";
|
|
30
|
+
import {Errors} from "../logging/log";
|
|
31
|
+
import {PerseusError} from "../perseus-error";
|
|
32
|
+
|
|
33
|
+
import KhanColors from "./colors";
|
|
34
|
+
import GraphUtils from "./graphie";
|
|
35
|
+
import KhanMath from "./math";
|
|
36
|
+
|
|
37
|
+
import type {Coord} from "../interactive2/types";
|
|
38
|
+
|
|
39
|
+
const {getCanUse3dTransform} = InteractiveUtil;
|
|
40
|
+
|
|
41
|
+
function sum(array: any) {
|
|
42
|
+
return _.reduce(
|
|
43
|
+
array,
|
|
44
|
+
function (memo, arg) {
|
|
45
|
+
return memo + arg;
|
|
46
|
+
},
|
|
47
|
+
0,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function clockwise(points: any) {
|
|
52
|
+
const segments = _.zip(points, points.slice(1).concat(points.slice(0, 1)));
|
|
53
|
+
const areas = _.map(segments, function (segment) {
|
|
54
|
+
const p1 = segment[0];
|
|
55
|
+
const p2 = segment[1];
|
|
56
|
+
return (p2[0] - p1[0]) * (p2[1] + p1[1]);
|
|
57
|
+
});
|
|
58
|
+
return sum(areas) > 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* vector-add multiple [x, y] coords/vectors */
|
|
62
|
+
function addPoints(...points) {
|
|
63
|
+
const zipped = _.zip(...points);
|
|
64
|
+
return _.map(zipped, sum);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function reverseVector(vector: Array<number>) {
|
|
68
|
+
return _.map(vector, function (coord) {
|
|
69
|
+
return coord * -1;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function scaledDistanceFromAngle(angle: number) {
|
|
74
|
+
const a = 3.51470560176242 * 20;
|
|
75
|
+
const b = 0.5687298702748785 * 20;
|
|
76
|
+
const c = -0.037587715462826674;
|
|
77
|
+
return (a - b) * Math.exp(c * angle) + b;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function scaledPolarRad(radius: number, radians: number) {
|
|
81
|
+
return [
|
|
82
|
+
radius * Math.cos(radians),
|
|
83
|
+
radius * Math.sin(radians) * -1, // SVG flips y axis
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function scaledPolarDeg(radius: number, degrees) {
|
|
88
|
+
const radians = (degrees * Math.PI) / 180;
|
|
89
|
+
return scaledPolarRad(radius, radians);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Global dragging state
|
|
93
|
+
let dragging = false;
|
|
94
|
+
|
|
95
|
+
const InteractiveUtils: any = {
|
|
96
|
+
// Useful for shapes that are only sometimes drawn. If a shape isn't
|
|
97
|
+
// needed, it can be replaced with bogusShape which just has stub methods
|
|
98
|
+
// that successfully do nothing.
|
|
99
|
+
// The alternative would be 'if..typeof' checks all over the place.
|
|
100
|
+
bogusShape: {
|
|
101
|
+
animate: function () {},
|
|
102
|
+
attr: function () {},
|
|
103
|
+
remove: function () {},
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
_.extend(GraphUtils.Graphie.prototype, {
|
|
108
|
+
// graphie puts text spans on top of the SVG, which looks good, but gets
|
|
109
|
+
// in the way of mouse events. This adds another SVG element on top
|
|
110
|
+
// of everything else where we can add invisible shapes with mouse
|
|
111
|
+
// handlers wherever we want.
|
|
112
|
+
addMouseLayer: function (options) {
|
|
113
|
+
const graph = this;
|
|
114
|
+
options = _.extend(
|
|
115
|
+
{
|
|
116
|
+
allowScratchpad: false,
|
|
117
|
+
setDrawingAreaAvailable: function () {},
|
|
118
|
+
},
|
|
119
|
+
options,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const mouselayerZIndex = 2;
|
|
123
|
+
graph.mouselayer = Raphael(
|
|
124
|
+
graph.raphael.canvas.parentNode,
|
|
125
|
+
graph.xpixels,
|
|
126
|
+
graph.ypixels,
|
|
127
|
+
);
|
|
128
|
+
$(graph.mouselayer.canvas).css("z-index", mouselayerZIndex);
|
|
129
|
+
if (
|
|
130
|
+
options.onClick ||
|
|
131
|
+
options.onMouseDown ||
|
|
132
|
+
options.onMouseMove ||
|
|
133
|
+
options.onMouseOver ||
|
|
134
|
+
options.onMouseOut
|
|
135
|
+
) {
|
|
136
|
+
const canvasClickTarget = graph.mouselayer
|
|
137
|
+
.rect(0, 0, graph.xpixels, graph.ypixels)
|
|
138
|
+
.attr({
|
|
139
|
+
fill: "#000",
|
|
140
|
+
opacity: 0,
|
|
141
|
+
});
|
|
142
|
+
let isClickingCanvas = false;
|
|
143
|
+
|
|
144
|
+
$(graph.mouselayer.canvas).on("vmousedown", function (e) {
|
|
145
|
+
if (e.target === canvasClickTarget[0]) {
|
|
146
|
+
if (options.onMouseDown) {
|
|
147
|
+
options.onMouseDown(graph.getMouseCoord(e));
|
|
148
|
+
}
|
|
149
|
+
isClickingCanvas = true;
|
|
150
|
+
|
|
151
|
+
if (options.onMouseMove) {
|
|
152
|
+
$(document).bind("vmousemove.mouseLayer", function (e) {
|
|
153
|
+
if (isClickingCanvas) {
|
|
154
|
+
e.preventDefault();
|
|
155
|
+
options.onMouseMove(graph.getMouseCoord(e));
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
$(document).bind("vmouseup.mouseLayer", function (e) {
|
|
161
|
+
$(document).unbind(".mouseLayer");
|
|
162
|
+
|
|
163
|
+
// Only register clicks that started on the canvas,
|
|
164
|
+
// and not on another mouseLayer target
|
|
165
|
+
if (isClickingCanvas && options.onClick) {
|
|
166
|
+
options.onClick(graph.getMouseCoord(e));
|
|
167
|
+
}
|
|
168
|
+
isClickingCanvas = false;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
if (options.onMouseOver) {
|
|
173
|
+
$(graph.mouselayer.canvas).on("vmouseover", function (e) {
|
|
174
|
+
options.onMouseOver(graph.getMouseCoord(e));
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
if (options.onMouseOut) {
|
|
178
|
+
$(graph.mouselayer.canvas).on("vmouseout", function (e) {
|
|
179
|
+
options.onMouseOut(graph.getMouseCoord(e));
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (!options.allowScratchpad) {
|
|
184
|
+
options.setDrawingAreaAvailable(false);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Add mouse and visible wrapper layers for DOM-node-wrapped movables
|
|
188
|
+
graph._mouselayerWrapper = document.createElement("div");
|
|
189
|
+
$(graph._mouselayerWrapper).css({
|
|
190
|
+
position: "absolute",
|
|
191
|
+
left: 0,
|
|
192
|
+
top: 0,
|
|
193
|
+
zIndex: mouselayerZIndex,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
graph._visiblelayerWrapper = document.createElement("div");
|
|
197
|
+
$(graph._visiblelayerWrapper).css({
|
|
198
|
+
position: "absolute",
|
|
199
|
+
left: 0,
|
|
200
|
+
top: 0,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const el = graph.raphael.canvas.parentNode;
|
|
204
|
+
el.appendChild(graph._visiblelayerWrapper);
|
|
205
|
+
el.appendChild(graph._mouselayerWrapper);
|
|
206
|
+
|
|
207
|
+
// Add functions for adding to wrappers
|
|
208
|
+
graph.addToMouseLayerWrapper = function (el: any) {
|
|
209
|
+
this._mouselayerWrapper.appendChild(el);
|
|
210
|
+
};
|
|
211
|
+
graph.addToVisibleLayerWrapper = function (el: any) {
|
|
212
|
+
this._visiblelayerWrapper.appendChild(el);
|
|
213
|
+
};
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get mouse coordinates in pixels
|
|
218
|
+
*/
|
|
219
|
+
getMousePx: function (event) {
|
|
220
|
+
const graphie = this;
|
|
221
|
+
|
|
222
|
+
const mouseX =
|
|
223
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
224
|
+
event.pageX - $(graphie.raphael.canvas.parentNode).offset().left;
|
|
225
|
+
const mouseY =
|
|
226
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
227
|
+
event.pageY - $(graphie.raphael.canvas.parentNode).offset().top;
|
|
228
|
+
|
|
229
|
+
return [mouseX, mouseY];
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Get mouse coordinates in graph coordinates
|
|
234
|
+
*/
|
|
235
|
+
getMouseCoord: function (event) {
|
|
236
|
+
return this.unscalePoint(this.getMousePx(event));
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Unlike all other Graphie-related code, the following three functions use
|
|
241
|
+
* a lot of scaled coordinates (so that labels appear the same size
|
|
242
|
+
* regardless of current shape/figure scale). These are prefixed with 's'.
|
|
243
|
+
*/
|
|
244
|
+
labelAngle: function (options) {
|
|
245
|
+
const graphie = this;
|
|
246
|
+
|
|
247
|
+
_.defaults(options, {
|
|
248
|
+
point1: [0, 0],
|
|
249
|
+
vertex: [0, 0],
|
|
250
|
+
point3: [0, 0],
|
|
251
|
+
label: null,
|
|
252
|
+
numArcs: 1,
|
|
253
|
+
showRightAngleMarker: true,
|
|
254
|
+
pushOut: 0,
|
|
255
|
+
clockwise: false,
|
|
256
|
+
style: {},
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
let text = options.text === undefined ? "" : options.text;
|
|
260
|
+
const vertex = options.vertex;
|
|
261
|
+
const sVertex = graphie.scalePoint(vertex);
|
|
262
|
+
let p1;
|
|
263
|
+
let p3;
|
|
264
|
+
if (options.clockwise) {
|
|
265
|
+
p1 = options.point1;
|
|
266
|
+
p3 = options.point3;
|
|
267
|
+
} else {
|
|
268
|
+
p1 = options.point3;
|
|
269
|
+
p3 = options.point1;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const startAngle = GraphUtils.findAngle(p1, vertex);
|
|
273
|
+
const endAngle = GraphUtils.findAngle(p3, vertex);
|
|
274
|
+
const angle = (endAngle + 360 - startAngle) % 360;
|
|
275
|
+
const halfAngle = (startAngle + angle / 2) % 360;
|
|
276
|
+
const sPadding = 5 * options.pushOut;
|
|
277
|
+
let sRadius = sPadding + scaledDistanceFromAngle(angle);
|
|
278
|
+
const temp: Array<never> = [];
|
|
279
|
+
|
|
280
|
+
if (Math.abs(angle - 90) < 1e-9 && options.showRightAngleMarker) {
|
|
281
|
+
const v1 = addPoints(sVertex, scaledPolarDeg(sRadius, startAngle));
|
|
282
|
+
const v2 = addPoints(sVertex, scaledPolarDeg(sRadius, endAngle));
|
|
283
|
+
|
|
284
|
+
sRadius *= Math.SQRT2;
|
|
285
|
+
const v3 = addPoints(sVertex, scaledPolarDeg(sRadius, halfAngle));
|
|
286
|
+
|
|
287
|
+
_.each([v1, v2], function (v) {
|
|
288
|
+
// @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'any' is not assignable to parameter of type 'never'.
|
|
289
|
+
temp.push(graphie.scaledPath([v, v3], options.style));
|
|
290
|
+
});
|
|
291
|
+
} else {
|
|
292
|
+
// Draw arcs
|
|
293
|
+
_.times(options.numArcs, function (i) {
|
|
294
|
+
temp.push(
|
|
295
|
+
// @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'any' is not assignable to parameter of type 'never'.
|
|
296
|
+
graphie.arc(
|
|
297
|
+
vertex,
|
|
298
|
+
graphie.unscaleVector(sRadius),
|
|
299
|
+
startAngle,
|
|
300
|
+
endAngle,
|
|
301
|
+
options.style,
|
|
302
|
+
),
|
|
303
|
+
);
|
|
304
|
+
sRadius += 3;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (text) {
|
|
309
|
+
const match = text.match(/\$deg(\d)?/);
|
|
310
|
+
if (match) {
|
|
311
|
+
const precision = match[1] || 1;
|
|
312
|
+
text = text.replace(
|
|
313
|
+
match[0],
|
|
314
|
+
KhanMath.toFixedApprox(angle, precision) + "^{\\circ}",
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const sOffset = scaledPolarDeg(sRadius + 15, halfAngle);
|
|
319
|
+
const sPosition = addPoints(sVertex, sOffset);
|
|
320
|
+
const position = graphie.unscalePoint(sPosition);
|
|
321
|
+
|
|
322
|
+
// Reuse label if possible
|
|
323
|
+
if (options.label) {
|
|
324
|
+
options.label.setPosition(position);
|
|
325
|
+
options.label.processMath(text, /* force */ true);
|
|
326
|
+
} else {
|
|
327
|
+
graphie.label(position, text, "center", options.style);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return temp;
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
labelSide: function (options) {
|
|
335
|
+
const graphie = this;
|
|
336
|
+
|
|
337
|
+
_.defaults(options, {
|
|
338
|
+
point1: [0, 0],
|
|
339
|
+
point2: [0, 0],
|
|
340
|
+
label: null,
|
|
341
|
+
text: "",
|
|
342
|
+
numTicks: 0,
|
|
343
|
+
numArrows: 0,
|
|
344
|
+
clockwise: false,
|
|
345
|
+
style: {},
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
let p1;
|
|
349
|
+
let p2;
|
|
350
|
+
if (options.clockwise) {
|
|
351
|
+
p1 = options.point1;
|
|
352
|
+
p2 = options.point2;
|
|
353
|
+
} else {
|
|
354
|
+
p1 = options.point2;
|
|
355
|
+
p2 = options.point1;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const midpoint = [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2];
|
|
359
|
+
const sMidpoint = graphie.scalePoint(midpoint);
|
|
360
|
+
const parallelAngle = Math.atan2(p2[1] - p1[1], p2[0] - p1[0]);
|
|
361
|
+
const perpendicularAngle = parallelAngle + Math.PI / 2;
|
|
362
|
+
const temp: Array<never> = [];
|
|
363
|
+
let sCumulativeOffset = 0;
|
|
364
|
+
|
|
365
|
+
if (options.numTicks) {
|
|
366
|
+
const n = options.numTicks;
|
|
367
|
+
|
|
368
|
+
const sSpacing = 5;
|
|
369
|
+
const sHeight = 5;
|
|
370
|
+
|
|
371
|
+
const style = _.extend({}, options.style, {
|
|
372
|
+
strokeWidth: 2,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
_.times(n, function (i) {
|
|
376
|
+
const sOffset = sSpacing * (i - (n - 1) / 2);
|
|
377
|
+
|
|
378
|
+
const sOffsetVector = scaledPolarRad(sOffset, parallelAngle);
|
|
379
|
+
const sHeightVector = scaledPolarRad(
|
|
380
|
+
sHeight,
|
|
381
|
+
perpendicularAngle,
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
const sPath = [
|
|
385
|
+
addPoints(sMidpoint, sOffsetVector, sHeightVector),
|
|
386
|
+
addPoints(
|
|
387
|
+
sMidpoint,
|
|
388
|
+
sOffsetVector,
|
|
389
|
+
reverseVector(sHeightVector),
|
|
390
|
+
),
|
|
391
|
+
];
|
|
392
|
+
|
|
393
|
+
// @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'any' is not assignable to parameter of type 'never'.
|
|
394
|
+
temp.push(graphie.scaledPath(sPath, style));
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
sCumulativeOffset += sSpacing * (n - 1) + 15;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (options.numArrows) {
|
|
401
|
+
const n = options.numArrows;
|
|
402
|
+
|
|
403
|
+
const start = [p1, p2].sort(function (a, b) {
|
|
404
|
+
if (a[1] === b[1]) {
|
|
405
|
+
return a[0] - b[0];
|
|
406
|
+
}
|
|
407
|
+
return a[1] - b[1];
|
|
408
|
+
})[0];
|
|
409
|
+
const sStart = graphie.scalePoint(start);
|
|
410
|
+
|
|
411
|
+
const style = _.extend({}, options.style, {
|
|
412
|
+
arrows: "->",
|
|
413
|
+
strokeWidth: 2,
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
const sSpacing = 5;
|
|
417
|
+
|
|
418
|
+
_.times(n, function (i) {
|
|
419
|
+
const sOffset = sCumulativeOffset + sSpacing * i;
|
|
420
|
+
let sOffsetVector = scaledPolarRad(sOffset, parallelAngle);
|
|
421
|
+
|
|
422
|
+
if (start !== p1) {
|
|
423
|
+
sOffsetVector = reverseVector(sOffsetVector);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const sEnd = addPoints(sMidpoint, sOffsetVector);
|
|
427
|
+
|
|
428
|
+
// @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'any' is not assignable to parameter of type 'never'.
|
|
429
|
+
temp.push(graphie.scaledPath([sStart, sEnd], style));
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
let text = options.text;
|
|
434
|
+
if (text) {
|
|
435
|
+
const match = text.match(/\$len(\d)?/);
|
|
436
|
+
if (match) {
|
|
437
|
+
const distance = GraphUtils.getDistance(p1, p2);
|
|
438
|
+
const precision = match[1] || 1;
|
|
439
|
+
text = text.replace(
|
|
440
|
+
match[0],
|
|
441
|
+
KhanMath.toFixedApprox(distance, precision),
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const sOffset = 20;
|
|
446
|
+
const sOffsetVector = scaledPolarRad(sOffset, perpendicularAngle);
|
|
447
|
+
const sPosition = addPoints(sMidpoint, sOffsetVector);
|
|
448
|
+
const position = graphie.unscalePoint(sPosition);
|
|
449
|
+
|
|
450
|
+
// Reuse label if possible
|
|
451
|
+
if (options.label) {
|
|
452
|
+
options.label.setPosition(position);
|
|
453
|
+
options.label.processMath(text, /* force */ true);
|
|
454
|
+
} else {
|
|
455
|
+
graphie.label(position, text, "center", options.style);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return temp;
|
|
460
|
+
},
|
|
461
|
+
|
|
462
|
+
/* Can also be used to label points that aren't vertices */
|
|
463
|
+
labelVertex: function (options) {
|
|
464
|
+
const graphie = this;
|
|
465
|
+
|
|
466
|
+
_.defaults(options, {
|
|
467
|
+
point1: null,
|
|
468
|
+
vertex: [0, 0],
|
|
469
|
+
point3: null,
|
|
470
|
+
label: null,
|
|
471
|
+
text: "",
|
|
472
|
+
clockwise: false,
|
|
473
|
+
style: {},
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
if (!options.text) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const vertex = options.vertex;
|
|
481
|
+
const sVertex = graphie.scalePoint(vertex);
|
|
482
|
+
let p1;
|
|
483
|
+
let p3;
|
|
484
|
+
if (options.clockwise) {
|
|
485
|
+
p1 = options.point1;
|
|
486
|
+
p3 = options.point3;
|
|
487
|
+
} else {
|
|
488
|
+
p1 = options.point3;
|
|
489
|
+
p3 = options.point1;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
let angle = 135;
|
|
493
|
+
let halfAngle;
|
|
494
|
+
if (p1 && p3) {
|
|
495
|
+
const startAngle = GraphUtils.findAngle(p1, vertex);
|
|
496
|
+
const endAngle = GraphUtils.findAngle(p3, vertex);
|
|
497
|
+
angle = (endAngle + 360 - startAngle) % 360;
|
|
498
|
+
halfAngle = (startAngle + angle / 2 + 180) % 360;
|
|
499
|
+
} else if (p1) {
|
|
500
|
+
const parallelAngle = GraphUtils.findAngle(vertex, p1);
|
|
501
|
+
halfAngle = parallelAngle + 90;
|
|
502
|
+
} else if (p3) {
|
|
503
|
+
const parallelAngle = GraphUtils.findAngle(p3, vertex);
|
|
504
|
+
halfAngle = parallelAngle + 90;
|
|
505
|
+
} else {
|
|
506
|
+
// Standalone point
|
|
507
|
+
halfAngle = 135;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const sRadius = 10 + scaledDistanceFromAngle(360 - angle);
|
|
511
|
+
const sOffsetVector = scaledPolarDeg(sRadius, halfAngle);
|
|
512
|
+
const sPosition = addPoints(sVertex, sOffsetVector);
|
|
513
|
+
const position = graphie.unscalePoint(sPosition);
|
|
514
|
+
|
|
515
|
+
// Reuse label if possible
|
|
516
|
+
if (options.label) {
|
|
517
|
+
options.label.setPosition(position);
|
|
518
|
+
options.label.processMath(options.text, /* force */ true);
|
|
519
|
+
} else {
|
|
520
|
+
graphie.label(position, options.text, "center", options.style);
|
|
521
|
+
}
|
|
522
|
+
},
|
|
523
|
+
|
|
524
|
+
// Add a point to the graph that can be dragged around.
|
|
525
|
+
// It allows automatic constraints on its movement as well as automatically
|
|
526
|
+
// managing line segments that terminate at the point.
|
|
527
|
+
//
|
|
528
|
+
// Options can be set to control how the point behaves:
|
|
529
|
+
// coord[]:
|
|
530
|
+
// The initial position of the point
|
|
531
|
+
// snapX, snapY:
|
|
532
|
+
// The minimum increment the point can be moved
|
|
533
|
+
//
|
|
534
|
+
// The return value is an object that can be used to manipulate the point:
|
|
535
|
+
// The coordX and coordY properties tell you the current position
|
|
536
|
+
//
|
|
537
|
+
// By adding an onMove() method to the returned object, you can install an
|
|
538
|
+
// event handler that gets called every time the user moves the point.
|
|
539
|
+
//
|
|
540
|
+
// The returned object also provides a moveTo(x,y) method that will move
|
|
541
|
+
// the point to a specific coordinate
|
|
542
|
+
//
|
|
543
|
+
// Constraints can be set on the on the returned object:
|
|
544
|
+
//
|
|
545
|
+
// - Set point to be immovable:
|
|
546
|
+
// movablePoint.constraints.fixed = true
|
|
547
|
+
//
|
|
548
|
+
// - Constrain point to a fixed distance from another point. The resulting
|
|
549
|
+
// point will move in a circle:
|
|
550
|
+
// movablePoint.fixedDistance = {
|
|
551
|
+
// dist: 2,
|
|
552
|
+
// point: point1
|
|
553
|
+
// }
|
|
554
|
+
//
|
|
555
|
+
// - Constrain point to a line defined by a fixed angle between it and
|
|
556
|
+
// two other points:
|
|
557
|
+
// movablePoint.fixedAngle = {
|
|
558
|
+
// angle: 45,
|
|
559
|
+
// vertex: point1,
|
|
560
|
+
// ref: point2
|
|
561
|
+
// }
|
|
562
|
+
//
|
|
563
|
+
// - Confined the point to traveling in a vertical or horizontal line,
|
|
564
|
+
// respectively
|
|
565
|
+
// movablePoint.constrainX = true;
|
|
566
|
+
// movablePoint.constrainY = true;
|
|
567
|
+
//
|
|
568
|
+
// - Connect a movableLineSegment to a movablePoint. The point is attached
|
|
569
|
+
// to a specific end of the line segment by adding the segment either to
|
|
570
|
+
// the list of lines that start at the point or the list of lines that
|
|
571
|
+
// end at the point (movableLineSegment can do this for you):
|
|
572
|
+
// movablePoint.lineStarts.push(movableLineSegment);
|
|
573
|
+
// - or -
|
|
574
|
+
// movablePoint.lineEnds.push(movableLineSegment);
|
|
575
|
+
//
|
|
576
|
+
// - Connect a movablePolygon to a movablePoint in exacty the same way:
|
|
577
|
+
// movablePoint.polygonVertices.push(movablePolygon);
|
|
578
|
+
//
|
|
579
|
+
addMovablePoint: function (options) {
|
|
580
|
+
const movablePoint = $.extend(
|
|
581
|
+
true,
|
|
582
|
+
{
|
|
583
|
+
graph: this,
|
|
584
|
+
coord: [0, 0],
|
|
585
|
+
snapX: 0,
|
|
586
|
+
snapY: 0,
|
|
587
|
+
pointSize: 4,
|
|
588
|
+
highlight: false,
|
|
589
|
+
dragging: false,
|
|
590
|
+
visible: true,
|
|
591
|
+
bounded: true,
|
|
592
|
+
constraints: {
|
|
593
|
+
fixed: false,
|
|
594
|
+
constrainX: false,
|
|
595
|
+
constrainY: false,
|
|
596
|
+
fixedAngle: {},
|
|
597
|
+
fixedDistance: {},
|
|
598
|
+
},
|
|
599
|
+
lineStarts: [],
|
|
600
|
+
lineEnds: [],
|
|
601
|
+
polygonVertices: [],
|
|
602
|
+
normalStyle: {},
|
|
603
|
+
highlightStyle: {
|
|
604
|
+
fill: KhanColors.INTERACTING,
|
|
605
|
+
stroke: KhanColors.INTERACTING,
|
|
606
|
+
},
|
|
607
|
+
labelStyle: {
|
|
608
|
+
color: KhanColors.INTERACTIVE,
|
|
609
|
+
},
|
|
610
|
+
vertexLabel: "",
|
|
611
|
+
mouseTarget: null,
|
|
612
|
+
},
|
|
613
|
+
options,
|
|
614
|
+
);
|
|
615
|
+
|
|
616
|
+
const normalColor = movablePoint.constraints.fixed
|
|
617
|
+
? KhanColors.DYNAMIC
|
|
618
|
+
: KhanColors.INTERACTIVE;
|
|
619
|
+
movablePoint.normalStyle = _.extend(
|
|
620
|
+
{},
|
|
621
|
+
{
|
|
622
|
+
fill: normalColor,
|
|
623
|
+
stroke: normalColor,
|
|
624
|
+
},
|
|
625
|
+
options.normalStyle,
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
// deprecated: don't use coordX/coordY; use coord[]
|
|
629
|
+
if (options.coordX !== undefined) {
|
|
630
|
+
movablePoint.coord[0] = options.coordX;
|
|
631
|
+
}
|
|
632
|
+
if (options.coordY !== undefined) {
|
|
633
|
+
movablePoint.coord[1] = options.coordY;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const graph = movablePoint.graph;
|
|
637
|
+
|
|
638
|
+
const applySnapAndConstraints = function (coord: any) {
|
|
639
|
+
// coord should be the scaled coordinate
|
|
640
|
+
|
|
641
|
+
// move point away from edge of graph unless it's invisible or fixed
|
|
642
|
+
if (
|
|
643
|
+
movablePoint.visible &&
|
|
644
|
+
movablePoint.bounded &&
|
|
645
|
+
!movablePoint.constraints.fixed
|
|
646
|
+
) {
|
|
647
|
+
// can't go beyond 10 pixels from the edge
|
|
648
|
+
coord = graph.constrainToBounds(coord, 10);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
let coordX = coord[0];
|
|
652
|
+
let coordY = coord[1];
|
|
653
|
+
|
|
654
|
+
// snap coordinates to grid
|
|
655
|
+
if (movablePoint.snapX !== 0) {
|
|
656
|
+
coordX =
|
|
657
|
+
Math.round(coordX / movablePoint.snapX) *
|
|
658
|
+
movablePoint.snapX;
|
|
659
|
+
}
|
|
660
|
+
if (movablePoint.snapY !== 0) {
|
|
661
|
+
coordY =
|
|
662
|
+
Math.round(coordY / movablePoint.snapY) *
|
|
663
|
+
movablePoint.snapY;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// snap to points around circle
|
|
667
|
+
if (movablePoint.constraints.fixedDistance.snapPoints) {
|
|
668
|
+
const mouse = graph.scalePoint(coord);
|
|
669
|
+
let mouseX = mouse[0];
|
|
670
|
+
let mouseY = mouse[1];
|
|
671
|
+
|
|
672
|
+
const snapRadians =
|
|
673
|
+
(2 * Math.PI) /
|
|
674
|
+
movablePoint.constraints.fixedDistance.snapPoints;
|
|
675
|
+
const radius = movablePoint.constraints.fixedDistance.dist;
|
|
676
|
+
|
|
677
|
+
const centerCoord =
|
|
678
|
+
movablePoint.constraints.fixedDistance.point;
|
|
679
|
+
const centerX =
|
|
680
|
+
(centerCoord[0] - graph.range[0][0]) * graph.scale[0];
|
|
681
|
+
const centerY =
|
|
682
|
+
(-centerCoord[1] + graph.range[1][1]) * graph.scale[1];
|
|
683
|
+
|
|
684
|
+
let mouseXrel = mouseX - centerX;
|
|
685
|
+
let mouseYrel = -mouseY + centerY;
|
|
686
|
+
let radians = Math.atan(mouseYrel / mouseXrel);
|
|
687
|
+
const outsideArcTanRange = mouseXrel < 0;
|
|
688
|
+
|
|
689
|
+
// adjust so that angles increase from 0 to 2 pi as you go
|
|
690
|
+
// around the circle
|
|
691
|
+
if (outsideArcTanRange) {
|
|
692
|
+
radians += Math.PI;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// perform the snap
|
|
696
|
+
radians = Math.round(radians / snapRadians) * snapRadians;
|
|
697
|
+
|
|
698
|
+
// convert from radians back to pixels
|
|
699
|
+
mouseXrel = radius * Math.cos(radians);
|
|
700
|
+
mouseYrel = radius * Math.sin(radians);
|
|
701
|
+
// convert back to coordinates relative to graphie canvas
|
|
702
|
+
mouseX = mouseXrel + centerX;
|
|
703
|
+
mouseY = -mouseYrel + centerY;
|
|
704
|
+
coordX = KhanMath.roundTo(
|
|
705
|
+
5,
|
|
706
|
+
mouseX / graph.scale[0] + graph.range[0][0],
|
|
707
|
+
);
|
|
708
|
+
coordY = KhanMath.roundTo(
|
|
709
|
+
5,
|
|
710
|
+
graph.range[1][1] - mouseY / graph.scale[1],
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
const result = movablePoint.applyConstraint([coordX, coordY]);
|
|
715
|
+
return result;
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
// Using the passed coordinates, apply any constraints and return
|
|
719
|
+
// the closest coordinates that match the constraints.
|
|
720
|
+
movablePoint.applyConstraint = function (
|
|
721
|
+
coord: any,
|
|
722
|
+
extraConstraints: any,
|
|
723
|
+
override: any,
|
|
724
|
+
) {
|
|
725
|
+
let newCoord = coord.slice();
|
|
726
|
+
const constraints: Record<string, any> = {};
|
|
727
|
+
if (override) {
|
|
728
|
+
$.extend(
|
|
729
|
+
constraints,
|
|
730
|
+
{
|
|
731
|
+
fixed: false,
|
|
732
|
+
constrainX: false,
|
|
733
|
+
constrainY: false,
|
|
734
|
+
fixedAngle: {},
|
|
735
|
+
fixedDistance: {},
|
|
736
|
+
},
|
|
737
|
+
extraConstraints,
|
|
738
|
+
);
|
|
739
|
+
} else {
|
|
740
|
+
$.extend(constraints, this.constraints, extraConstraints);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// constrain to vertical movement
|
|
744
|
+
if (constraints.constrainX) {
|
|
745
|
+
newCoord = [this.coord[0], coord[1]];
|
|
746
|
+
|
|
747
|
+
// constrain to horizontal movement
|
|
748
|
+
} else if (constraints.constrainY) {
|
|
749
|
+
newCoord = [coord[0], this.coord[1]];
|
|
750
|
+
|
|
751
|
+
// both distance and angle are constrained
|
|
752
|
+
} else if (
|
|
753
|
+
typeof constraints.fixedAngle.angle === "number" &&
|
|
754
|
+
typeof constraints.fixedDistance.dist === "number"
|
|
755
|
+
) {
|
|
756
|
+
const vertex =
|
|
757
|
+
constraints.fixedAngle.vertex.coord ||
|
|
758
|
+
constraints.fixedAngle.vertex;
|
|
759
|
+
const ref =
|
|
760
|
+
constraints.fixedAngle.ref.coord ||
|
|
761
|
+
constraints.fixedAngle.ref;
|
|
762
|
+
const distPoint =
|
|
763
|
+
constraints.fixedDistance.point.coord ||
|
|
764
|
+
constraints.fixedDistance.point;
|
|
765
|
+
|
|
766
|
+
const constrainedAngle =
|
|
767
|
+
((constraints.fixedAngle.angle +
|
|
768
|
+
GraphUtils.findAngle(ref, vertex)) *
|
|
769
|
+
Math.PI) /
|
|
770
|
+
180;
|
|
771
|
+
const length = constraints.fixedDistance.dist;
|
|
772
|
+
newCoord[0] =
|
|
773
|
+
length * Math.cos(constrainedAngle) + distPoint[0];
|
|
774
|
+
newCoord[1] =
|
|
775
|
+
length * Math.sin(constrainedAngle) + distPoint[1];
|
|
776
|
+
|
|
777
|
+
// angle is constrained
|
|
778
|
+
} else if (typeof constraints.fixedAngle.angle === "number") {
|
|
779
|
+
const vertex =
|
|
780
|
+
constraints.fixedAngle.vertex.coord ||
|
|
781
|
+
constraints.fixedAngle.vertex;
|
|
782
|
+
const ref =
|
|
783
|
+
constraints.fixedAngle.ref.coord ||
|
|
784
|
+
constraints.fixedAngle.ref;
|
|
785
|
+
|
|
786
|
+
const constrainedAngle =
|
|
787
|
+
((constraints.fixedAngle.angle +
|
|
788
|
+
GraphUtils.findAngle(ref, vertex)) *
|
|
789
|
+
Math.PI) /
|
|
790
|
+
180;
|
|
791
|
+
const angle =
|
|
792
|
+
(GraphUtils.findAngle(coord, vertex) * Math.PI) / 180;
|
|
793
|
+
const distance = GraphUtils.getDistance(coord, vertex);
|
|
794
|
+
let length = distance * Math.cos(constrainedAngle - angle);
|
|
795
|
+
length = length < 1.0 ? 1.0 : length;
|
|
796
|
+
newCoord[0] = length * Math.cos(constrainedAngle) + vertex[0];
|
|
797
|
+
newCoord[1] = length * Math.sin(constrainedAngle) + vertex[1];
|
|
798
|
+
|
|
799
|
+
// distance is constrained
|
|
800
|
+
} else if (typeof constraints.fixedDistance.dist === "number") {
|
|
801
|
+
const distPoint =
|
|
802
|
+
constraints.fixedDistance.point.coord ||
|
|
803
|
+
constraints.fixedDistance.point;
|
|
804
|
+
|
|
805
|
+
let angle = GraphUtils.findAngle(coord, distPoint);
|
|
806
|
+
const length = constraints.fixedDistance.dist;
|
|
807
|
+
angle = (angle * Math.PI) / 180;
|
|
808
|
+
newCoord[0] = length * Math.cos(angle) + distPoint[0];
|
|
809
|
+
newCoord[1] = length * Math.sin(angle) + distPoint[1];
|
|
810
|
+
|
|
811
|
+
// point is fixed
|
|
812
|
+
} else if (constraints.fixed) {
|
|
813
|
+
newCoord = movablePoint.coord;
|
|
814
|
+
}
|
|
815
|
+
return newCoord;
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
movablePoint.coord = applySnapAndConstraints(movablePoint.coord);
|
|
819
|
+
|
|
820
|
+
const highlightScale = 2;
|
|
821
|
+
|
|
822
|
+
if (movablePoint.visible) {
|
|
823
|
+
graph.style(movablePoint.normalStyle, function () {
|
|
824
|
+
const radii = [
|
|
825
|
+
movablePoint.pointSize / graph.scale[0],
|
|
826
|
+
movablePoint.pointSize / graph.scale[1],
|
|
827
|
+
];
|
|
828
|
+
const options = {
|
|
829
|
+
maxScale: highlightScale,
|
|
830
|
+
// Add in 2px of padding to avoid clipping at the edges.
|
|
831
|
+
padding: 2,
|
|
832
|
+
} as const;
|
|
833
|
+
movablePoint.visibleShape = new WrappedEllipse(
|
|
834
|
+
graph,
|
|
835
|
+
movablePoint.coord,
|
|
836
|
+
// @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'number[]' is not assignable to parameter of type '[number, number]'.
|
|
837
|
+
radii,
|
|
838
|
+
options,
|
|
839
|
+
);
|
|
840
|
+
movablePoint.visibleShape.attr(
|
|
841
|
+
_.omit(movablePoint.normalStyle, "scale"),
|
|
842
|
+
);
|
|
843
|
+
movablePoint.visibleShape.toFront();
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
movablePoint.normalStyle.scale = 1;
|
|
847
|
+
movablePoint.highlightStyle.scale = highlightScale;
|
|
848
|
+
|
|
849
|
+
if (movablePoint.vertexLabel) {
|
|
850
|
+
movablePoint.labeledVertex = this.label(
|
|
851
|
+
[0, 0],
|
|
852
|
+
"",
|
|
853
|
+
"center",
|
|
854
|
+
movablePoint.labelStyle,
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
movablePoint.drawLabel = function () {
|
|
859
|
+
if (movablePoint.vertexLabel) {
|
|
860
|
+
movablePoint.graph.labelVertex({
|
|
861
|
+
vertex: movablePoint.coord,
|
|
862
|
+
label: movablePoint.labeledVertex,
|
|
863
|
+
text: movablePoint.vertexLabel,
|
|
864
|
+
style: movablePoint.labelStyle,
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
|
|
869
|
+
movablePoint.drawLabel();
|
|
870
|
+
|
|
871
|
+
movablePoint.grab = function (offset) {
|
|
872
|
+
// The offset for the gesture. When provided, the movable point will
|
|
873
|
+
// track the mouse's position, plus this offset. This is typically
|
|
874
|
+
// used to lock the distance between a user's finger and the movable
|
|
875
|
+
// point, when dragging.
|
|
876
|
+
offset = offset || [0, 0];
|
|
877
|
+
|
|
878
|
+
$(document).bind(
|
|
879
|
+
"vmousemove.point vmouseup.point",
|
|
880
|
+
function (event) {
|
|
881
|
+
event.preventDefault();
|
|
882
|
+
movablePoint.dragging = true;
|
|
883
|
+
dragging = true;
|
|
884
|
+
|
|
885
|
+
// Adjust the target coordinate by accounting for the gesture's
|
|
886
|
+
// offset.
|
|
887
|
+
let coord = kvector.add(graph.getMouseCoord(event), offset);
|
|
888
|
+
|
|
889
|
+
coord = applySnapAndConstraints(coord);
|
|
890
|
+
let coordX = coord[0];
|
|
891
|
+
let coordY = coord[1];
|
|
892
|
+
let mouseX;
|
|
893
|
+
let mouseY;
|
|
894
|
+
|
|
895
|
+
if (event.type === "vmousemove") {
|
|
896
|
+
let doMove = true;
|
|
897
|
+
// The caller has the option of adding an onMove() method
|
|
898
|
+
// to the movablePoint object we return as a sort of event
|
|
899
|
+
// handler. By returning false from onMove(), the move
|
|
900
|
+
// can be vetoed, providing custom constraints on where
|
|
901
|
+
// the point can be moved. By returning array [x, y], the
|
|
902
|
+
// move can be overridden
|
|
903
|
+
if (_.isFunction(movablePoint.onMove)) {
|
|
904
|
+
const result = movablePoint.onMove(coordX, coordY);
|
|
905
|
+
if (result === false) {
|
|
906
|
+
doMove = false;
|
|
907
|
+
}
|
|
908
|
+
if (_.isArray(result)) {
|
|
909
|
+
coordX = result[0];
|
|
910
|
+
coordY = result[1];
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
// coord{X|Y} may have been modified by constraints or
|
|
914
|
+
// onMove handler; adjust mouse{X|Y} to match
|
|
915
|
+
mouseX = (coordX - graph.range[0][0]) * graph.scale[0];
|
|
916
|
+
mouseY = (-coordY + graph.range[1][1]) * graph.scale[1];
|
|
917
|
+
|
|
918
|
+
if (doMove) {
|
|
919
|
+
const point = graph.unscalePoint([mouseX, mouseY]);
|
|
920
|
+
movablePoint.visibleShape.moveTo(point);
|
|
921
|
+
movablePoint.mouseTarget.moveTo(point);
|
|
922
|
+
movablePoint.coord = [coordX, coordY];
|
|
923
|
+
movablePoint.updateLineEnds();
|
|
924
|
+
$(movablePoint).trigger("move");
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
movablePoint.drawLabel();
|
|
928
|
+
} else if (event.type === "vmouseup") {
|
|
929
|
+
$(document).unbind(".point");
|
|
930
|
+
movablePoint.dragging = false;
|
|
931
|
+
dragging = false;
|
|
932
|
+
if (_.isFunction(movablePoint.onMoveEnd)) {
|
|
933
|
+
const result = movablePoint.onMoveEnd(
|
|
934
|
+
coordX,
|
|
935
|
+
coordY,
|
|
936
|
+
);
|
|
937
|
+
if (_.isArray(result)) {
|
|
938
|
+
coordX = result[0];
|
|
939
|
+
coordY = result[1];
|
|
940
|
+
mouseX =
|
|
941
|
+
(coordX - graph.range[0][0]) *
|
|
942
|
+
graph.scale[0];
|
|
943
|
+
mouseY =
|
|
944
|
+
(-coordY + graph.range[1][1]) *
|
|
945
|
+
graph.scale[1];
|
|
946
|
+
const point = graph.unscalePoint([
|
|
947
|
+
mouseX,
|
|
948
|
+
mouseY,
|
|
949
|
+
]);
|
|
950
|
+
movablePoint.visibleShape.moveTo(point);
|
|
951
|
+
movablePoint.mouseTarget.moveTo(point);
|
|
952
|
+
movablePoint.coord = [coordX, coordY];
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
if (!movablePoint.highlight) {
|
|
956
|
+
movablePoint.visibleShape.animate(
|
|
957
|
+
movablePoint.normalStyle,
|
|
958
|
+
50,
|
|
959
|
+
);
|
|
960
|
+
if (movablePoint.onUnhighlight) {
|
|
961
|
+
movablePoint.onUnhighlight();
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
},
|
|
966
|
+
);
|
|
967
|
+
};
|
|
968
|
+
|
|
969
|
+
if (movablePoint.visible && !movablePoint.constraints.fixed) {
|
|
970
|
+
// the invisible shape in front of the point that gets mouse events
|
|
971
|
+
if (!movablePoint.mouseTarget) {
|
|
972
|
+
const radii = graph.unscaleVector(16);
|
|
973
|
+
const options = {
|
|
974
|
+
mouselayer: true,
|
|
975
|
+
padding: 0,
|
|
976
|
+
disableMouseEventsOnWrapper: true,
|
|
977
|
+
interactiveKindForTesting: "movable-point",
|
|
978
|
+
} as const;
|
|
979
|
+
movablePoint.mouseTarget = new WrappedEllipse(
|
|
980
|
+
graph,
|
|
981
|
+
movablePoint.coord,
|
|
982
|
+
radii,
|
|
983
|
+
options,
|
|
984
|
+
);
|
|
985
|
+
movablePoint.mouseTarget.attr({fill: "#000", opacity: 0.0});
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
const $mouseTarget = $(movablePoint.mouseTarget.getMouseTarget());
|
|
989
|
+
$mouseTarget.css("cursor", "move");
|
|
990
|
+
$mouseTarget.bind(
|
|
991
|
+
"vmousedown vmouseover vmouseout",
|
|
992
|
+
function (event) {
|
|
993
|
+
if (event.type === "vmouseover") {
|
|
994
|
+
movablePoint.highlight = true;
|
|
995
|
+
if (!dragging) {
|
|
996
|
+
movablePoint.visibleShape.animate(
|
|
997
|
+
movablePoint.highlightStyle,
|
|
998
|
+
50,
|
|
999
|
+
);
|
|
1000
|
+
if (movablePoint.onHighlight) {
|
|
1001
|
+
movablePoint.onHighlight();
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
} else if (event.type === "vmouseout") {
|
|
1005
|
+
movablePoint.highlight = false;
|
|
1006
|
+
if (!movablePoint.dragging && !dragging) {
|
|
1007
|
+
movablePoint.visibleShape.animate(
|
|
1008
|
+
movablePoint.normalStyle,
|
|
1009
|
+
50,
|
|
1010
|
+
);
|
|
1011
|
+
if (movablePoint.onUnhighlight) {
|
|
1012
|
+
movablePoint.onUnhighlight();
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
} else if (
|
|
1016
|
+
event.type === "vmousedown" &&
|
|
1017
|
+
(event.which === 1 || event.which === 0)
|
|
1018
|
+
) {
|
|
1019
|
+
event.preventDefault();
|
|
1020
|
+
|
|
1021
|
+
// The offset between the cursor or finger and the initial
|
|
1022
|
+
// coordinates of the point. This is tracked so as to avoid
|
|
1023
|
+
// locking the moving point to the user's finger on touch
|
|
1024
|
+
// devices, which would obscure it, no matter how large we
|
|
1025
|
+
// made the touch target. Instead, we respect the offset at
|
|
1026
|
+
// which the point was grabbed for the entirety of the
|
|
1027
|
+
// gesture, if it's a touch-based interaction.
|
|
1028
|
+
const startCoord = movablePoint.coord;
|
|
1029
|
+
const startMouseCoord = graph.getMouseCoord(event);
|
|
1030
|
+
const isMouse = !("ontouchstart" in window);
|
|
1031
|
+
const touchOffset = isMouse
|
|
1032
|
+
? [0, 0]
|
|
1033
|
+
: kvector.subtract(startCoord, startMouseCoord);
|
|
1034
|
+
|
|
1035
|
+
movablePoint.grab(touchOffset);
|
|
1036
|
+
}
|
|
1037
|
+
},
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// Method to let the caller animate the point to a new position.
|
|
1042
|
+
// Useful as part of a hint to show the user the correct place
|
|
1043
|
+
// to put the point.
|
|
1044
|
+
movablePoint.moveTo = function (
|
|
1045
|
+
coordX: any,
|
|
1046
|
+
coordY: any,
|
|
1047
|
+
updateLines: any,
|
|
1048
|
+
) {
|
|
1049
|
+
const distance = GraphUtils.getDistance(
|
|
1050
|
+
this.graph.scalePoint([coordX, coordY]),
|
|
1051
|
+
this.graph.scalePoint(this.coord),
|
|
1052
|
+
);
|
|
1053
|
+
|
|
1054
|
+
const time = distance * 5;
|
|
1055
|
+
|
|
1056
|
+
const cb =
|
|
1057
|
+
updateLines &&
|
|
1058
|
+
function (coord: any) {
|
|
1059
|
+
movablePoint.coord = coord;
|
|
1060
|
+
movablePoint.updateLineEnds();
|
|
1061
|
+
};
|
|
1062
|
+
this.visibleShape.animateTo([coordX, coordY], time, cb);
|
|
1063
|
+
this.mouseTarget.animateTo([coordX, coordY], time, cb);
|
|
1064
|
+
this.coord = [coordX, coordY];
|
|
1065
|
+
if (_.isFunction(this.onMove)) {
|
|
1066
|
+
this.onMove(coordX, coordY);
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
|
|
1070
|
+
// After moving the point, call this to update all line segments
|
|
1071
|
+
// terminating at the point
|
|
1072
|
+
movablePoint.updateLineEnds = function () {
|
|
1073
|
+
$(this.lineStarts).each(function () {
|
|
1074
|
+
this.coordA = movablePoint.coord;
|
|
1075
|
+
this.transform();
|
|
1076
|
+
});
|
|
1077
|
+
$(this.lineEnds).each(function () {
|
|
1078
|
+
this.coordZ = movablePoint.coord;
|
|
1079
|
+
this.transform();
|
|
1080
|
+
});
|
|
1081
|
+
$(this.polygonVertices).each(function () {
|
|
1082
|
+
this.transform();
|
|
1083
|
+
});
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
// Put the point at a new position without any checks, animation,
|
|
1087
|
+
// or callbacks
|
|
1088
|
+
movablePoint.setCoord = function (coord: any) {
|
|
1089
|
+
if (this.visible) {
|
|
1090
|
+
this.visibleShape.moveTo(coord);
|
|
1091
|
+
if (this.mouseTarget != null) {
|
|
1092
|
+
this.mouseTarget.moveTo(coord);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
this.coord = coord.slice();
|
|
1096
|
+
};
|
|
1097
|
+
|
|
1098
|
+
// Put the point at the new position, checking that it is
|
|
1099
|
+
// within the graph's bounds
|
|
1100
|
+
movablePoint.setCoordConstrained = function (coord: any) {
|
|
1101
|
+
this.setCoord(applySnapAndConstraints(coord));
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
// Change z-order to back
|
|
1105
|
+
movablePoint.toBack = function () {
|
|
1106
|
+
if (this.visible) {
|
|
1107
|
+
if (this.mouseTarget != null) {
|
|
1108
|
+
this.mouseTarget.toBack();
|
|
1109
|
+
}
|
|
1110
|
+
this.visibleShape.toBack();
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1114
|
+
// Change z-order to front
|
|
1115
|
+
movablePoint.toFront = function () {
|
|
1116
|
+
if (this.visible) {
|
|
1117
|
+
if (this.mouseTarget != null) {
|
|
1118
|
+
this.mouseTarget.toFront();
|
|
1119
|
+
}
|
|
1120
|
+
this.visibleShape.toFront();
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
movablePoint.remove = function () {
|
|
1125
|
+
if (this.visibleShape) {
|
|
1126
|
+
this.visibleShape.remove();
|
|
1127
|
+
}
|
|
1128
|
+
if (this.mouseTarget) {
|
|
1129
|
+
this.mouseTarget.remove();
|
|
1130
|
+
}
|
|
1131
|
+
if (this.labeledVertex) {
|
|
1132
|
+
this.labeledVertex.remove();
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
|
|
1136
|
+
return movablePoint;
|
|
1137
|
+
},
|
|
1138
|
+
|
|
1139
|
+
// MovableLineSegment is a line segment that can be dragged around the
|
|
1140
|
+
// screen. By attaching a smartPoint to each (or one) end, the ends can be
|
|
1141
|
+
// manipulated individually.
|
|
1142
|
+
//
|
|
1143
|
+
// To use with smartPoints, add the smartPoints first, then:
|
|
1144
|
+
// addMovableLineSegment({ pointA: smartPoint1, pointZ: smartPoint2 });
|
|
1145
|
+
// Or just one end:
|
|
1146
|
+
// addMovableLineSegment({ pointA: smartPoint, coordZ: [0, 0] });
|
|
1147
|
+
//
|
|
1148
|
+
// Include "fixed: true" in the options if you don't want the entire line
|
|
1149
|
+
// to be draggable (you can still use points to make the endpoints
|
|
1150
|
+
// draggable)
|
|
1151
|
+
//
|
|
1152
|
+
// The returned object includes the following properties/methods:
|
|
1153
|
+
//
|
|
1154
|
+
// - lineSegment.coordA / lineSegment.coordZ
|
|
1155
|
+
// The coordinates of each end of the line segment
|
|
1156
|
+
//
|
|
1157
|
+
// - lineSegment.transform(syncToPoints)
|
|
1158
|
+
// Repositions the line segment. Call after changing coordA
|
|
1159
|
+
// and/or coordZ, or pass syncToPoints = true to use the current
|
|
1160
|
+
// position of the corresponding smartPoints, if the segment was
|
|
1161
|
+
// defined using smartPoints
|
|
1162
|
+
//
|
|
1163
|
+
addMovableLineSegment: function (options: any) {
|
|
1164
|
+
const lineSegment = $.extend(
|
|
1165
|
+
{
|
|
1166
|
+
graph: this,
|
|
1167
|
+
coordA: [0, 0],
|
|
1168
|
+
coordZ: [1, 1],
|
|
1169
|
+
snapX: 0,
|
|
1170
|
+
snapY: 0,
|
|
1171
|
+
fixed: false,
|
|
1172
|
+
ticks: 0,
|
|
1173
|
+
normalStyle: {},
|
|
1174
|
+
highlightStyle: {
|
|
1175
|
+
stroke: KhanColors.INTERACTING,
|
|
1176
|
+
"stroke-width": 6,
|
|
1177
|
+
},
|
|
1178
|
+
labelStyle: {
|
|
1179
|
+
stroke: KhanColors.INTERACTIVE,
|
|
1180
|
+
color: KhanColors.INTERACTIVE,
|
|
1181
|
+
},
|
|
1182
|
+
highlight: false,
|
|
1183
|
+
dragging: false,
|
|
1184
|
+
tick: [],
|
|
1185
|
+
extendLine: false,
|
|
1186
|
+
extendRay: false,
|
|
1187
|
+
constraints: {
|
|
1188
|
+
fixed: false,
|
|
1189
|
+
constrainX: false,
|
|
1190
|
+
constrainY: false,
|
|
1191
|
+
},
|
|
1192
|
+
sideLabel: "",
|
|
1193
|
+
vertexLabels: [],
|
|
1194
|
+
numArrows: 0,
|
|
1195
|
+
numTicks: 0,
|
|
1196
|
+
movePointsWithLine: false,
|
|
1197
|
+
},
|
|
1198
|
+
options,
|
|
1199
|
+
);
|
|
1200
|
+
|
|
1201
|
+
const normalColor = lineSegment.fixed
|
|
1202
|
+
? KhanColors.DYNAMIC
|
|
1203
|
+
: KhanColors.INTERACTIVE;
|
|
1204
|
+
lineSegment.normalStyle = _.extend(
|
|
1205
|
+
{},
|
|
1206
|
+
{
|
|
1207
|
+
"stroke-width": 2,
|
|
1208
|
+
stroke: normalColor,
|
|
1209
|
+
},
|
|
1210
|
+
options.normalStyle,
|
|
1211
|
+
);
|
|
1212
|
+
// arrowStyle should be kept in sync with styling of the line
|
|
1213
|
+
lineSegment.arrowStyle = _.extend({}, lineSegment.normalStyle, {
|
|
1214
|
+
color: lineSegment.normalStyle.stroke,
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
// If the line segment is defined by movablePoints, coordA/coordZ are
|
|
1218
|
+
// owned by the points, otherwise they're owned by us
|
|
1219
|
+
if (options.pointA !== undefined) {
|
|
1220
|
+
lineSegment.coordA = options.pointA.coord;
|
|
1221
|
+
lineSegment.pointA.lineStarts.push(lineSegment);
|
|
1222
|
+
} else if (options.coordA !== undefined) {
|
|
1223
|
+
lineSegment.coordA = options.coordA.slice();
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
if (options.pointZ !== undefined) {
|
|
1227
|
+
lineSegment.coordZ = options.pointZ.coord;
|
|
1228
|
+
lineSegment.pointZ.lineEnds.push(lineSegment);
|
|
1229
|
+
} else if (options.coordA !== undefined) {
|
|
1230
|
+
lineSegment.coordA = lineSegment.coordA.slice();
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
const graph = lineSegment.graph;
|
|
1234
|
+
|
|
1235
|
+
graph.style(lineSegment.normalStyle);
|
|
1236
|
+
for (let i = 0; i < lineSegment.ticks; ++i) {
|
|
1237
|
+
lineSegment.tick[i] = InteractiveUtils.bogusShape;
|
|
1238
|
+
}
|
|
1239
|
+
let path = GraphUtils.unscaledSvgPath([
|
|
1240
|
+
[0, 0],
|
|
1241
|
+
[1, 0],
|
|
1242
|
+
]);
|
|
1243
|
+
for (let i = 0; i < lineSegment.ticks; ++i) {
|
|
1244
|
+
const tickoffset =
|
|
1245
|
+
0.5 - (lineSegment.ticks - 1 + i * 2) / graph.scale[0];
|
|
1246
|
+
// TODO(kevinb) figure out why path isn't being used
|
|
1247
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1248
|
+
path += GraphUtils.unscaledSvgPath([
|
|
1249
|
+
[tickoffset, -7],
|
|
1250
|
+
[tickoffset, 7],
|
|
1251
|
+
]);
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
options = {
|
|
1255
|
+
thickness: Math.max(
|
|
1256
|
+
lineSegment.normalStyle["stroke-width"],
|
|
1257
|
+
lineSegment.highlightStyle["stroke-width"],
|
|
1258
|
+
),
|
|
1259
|
+
};
|
|
1260
|
+
lineSegment.visibleLine = new WrappedLine(
|
|
1261
|
+
graph,
|
|
1262
|
+
[0, 0],
|
|
1263
|
+
[1, 0],
|
|
1264
|
+
options,
|
|
1265
|
+
);
|
|
1266
|
+
lineSegment.visibleLine.attr(lineSegment.normalStyle);
|
|
1267
|
+
|
|
1268
|
+
// Add mouse target
|
|
1269
|
+
if (!lineSegment.fixed) {
|
|
1270
|
+
const options = {
|
|
1271
|
+
thickness: 30,
|
|
1272
|
+
mouselayer: true,
|
|
1273
|
+
interactiveKindForTesting: "movable-line",
|
|
1274
|
+
} as const;
|
|
1275
|
+
lineSegment.mouseTarget = new WrappedLine(
|
|
1276
|
+
graph,
|
|
1277
|
+
[0, 0],
|
|
1278
|
+
[1, 0],
|
|
1279
|
+
options,
|
|
1280
|
+
);
|
|
1281
|
+
lineSegment.mouseTarget.attr({fill: "#000", opacity: 0.0});
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// Reposition the line segment. Call after changing coordA and/or
|
|
1285
|
+
// coordZ, or pass syncToPoints = true to use the current position of
|
|
1286
|
+
// the corresponding movablePoints, if the segment was defined using
|
|
1287
|
+
// movablePoints
|
|
1288
|
+
lineSegment.transform = function (syncToPoints) {
|
|
1289
|
+
if (syncToPoints) {
|
|
1290
|
+
if (typeof this.pointA === "object") {
|
|
1291
|
+
this.coordA = this.pointA.coord;
|
|
1292
|
+
}
|
|
1293
|
+
if (typeof this.pointZ === "object") {
|
|
1294
|
+
this.coordZ = this.pointZ.coord;
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
const getScaledAngle = function (line) {
|
|
1299
|
+
const scaledA = line.graph.scalePoint(line.coordA);
|
|
1300
|
+
const scaledZ = line.graph.scalePoint(line.coordZ);
|
|
1301
|
+
return kvector.polarDegFromCart(
|
|
1302
|
+
kvector.subtract(scaledZ, scaledA),
|
|
1303
|
+
)[1];
|
|
1304
|
+
};
|
|
1305
|
+
|
|
1306
|
+
const getClipPoint = function (graph: any, coord, angle: number) {
|
|
1307
|
+
graph = lineSegment.graph;
|
|
1308
|
+
const xExtent = graph.range[0][1] - graph.range[0][0];
|
|
1309
|
+
const yExtent = graph.range[1][1] - graph.range[1][0];
|
|
1310
|
+
|
|
1311
|
+
const distance = xExtent + yExtent;
|
|
1312
|
+
const angleVec = graph.unscaleVector(
|
|
1313
|
+
kvector.cartFromPolarDeg(1, angle),
|
|
1314
|
+
);
|
|
1315
|
+
const distVec = kvector.scale(
|
|
1316
|
+
kvector.normalize(angleVec),
|
|
1317
|
+
distance,
|
|
1318
|
+
);
|
|
1319
|
+
const farCoord = kvector.add(coord, distVec);
|
|
1320
|
+
const scaledAngle = kvector.polarDegFromCart(angleVec)[1];
|
|
1321
|
+
const clipPoint = graph.constrainToBoundsOnAngle(
|
|
1322
|
+
farCoord,
|
|
1323
|
+
4,
|
|
1324
|
+
(scaledAngle * Math.PI) / 180,
|
|
1325
|
+
);
|
|
1326
|
+
return clipPoint;
|
|
1327
|
+
};
|
|
1328
|
+
|
|
1329
|
+
const angle = getScaledAngle(this);
|
|
1330
|
+
let start = this.coordA;
|
|
1331
|
+
let end = this.coordZ;
|
|
1332
|
+
|
|
1333
|
+
// Extend start, end if necessary (i.e., if not a line segment)
|
|
1334
|
+
if (this.extendLine) {
|
|
1335
|
+
start = getClipPoint(graph, start, 360 - angle);
|
|
1336
|
+
end = getClipPoint(graph, end, (540 - angle) % 360);
|
|
1337
|
+
} else if (this.extendRay) {
|
|
1338
|
+
end = getClipPoint(graph, start, 360 - angle);
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
const elements = [this.visibleLine];
|
|
1342
|
+
if (!this.fixed) {
|
|
1343
|
+
elements.push(this.mouseTarget);
|
|
1344
|
+
}
|
|
1345
|
+
_.each(elements, function (element) {
|
|
1346
|
+
element.moveTo(start, end);
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1349
|
+
const createArrow = function (graph: any, style) {
|
|
1350
|
+
const center = [0.75, 0];
|
|
1351
|
+
let points = [
|
|
1352
|
+
[-3, 4],
|
|
1353
|
+
[-2.75, 2.5],
|
|
1354
|
+
[0, 0.25],
|
|
1355
|
+
center,
|
|
1356
|
+
[0, -0.25],
|
|
1357
|
+
[-2.75, -2.5],
|
|
1358
|
+
[-3, -4],
|
|
1359
|
+
];
|
|
1360
|
+
|
|
1361
|
+
const scale = 1.4;
|
|
1362
|
+
points = _.map(points, function (point) {
|
|
1363
|
+
const pv = kvector.subtract(point, center);
|
|
1364
|
+
const pvScaled = kvector.scale(pv, scale);
|
|
1365
|
+
return kvector.add(center, pvScaled);
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
const createCubicPath = function (points: any) {
|
|
1369
|
+
let path = "M" + points[0][0] + " " + points[0][1];
|
|
1370
|
+
for (let i = 1; i < points.length; i += 3) {
|
|
1371
|
+
path +=
|
|
1372
|
+
"C" +
|
|
1373
|
+
points[i][0] +
|
|
1374
|
+
" " +
|
|
1375
|
+
points[i][1] +
|
|
1376
|
+
" " +
|
|
1377
|
+
points[i + 1][0] +
|
|
1378
|
+
" " +
|
|
1379
|
+
points[i + 1][1] +
|
|
1380
|
+
" " +
|
|
1381
|
+
points[i + 2][0] +
|
|
1382
|
+
" " +
|
|
1383
|
+
points[i + 2][1];
|
|
1384
|
+
}
|
|
1385
|
+
return path;
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
const unscaledPoints = _.map(points, graph.unscalePoint);
|
|
1389
|
+
const options = {
|
|
1390
|
+
center: graph.unscalePoint(center),
|
|
1391
|
+
createPath: createCubicPath,
|
|
1392
|
+
} as const;
|
|
1393
|
+
const arrowHead = new WrappedPath(
|
|
1394
|
+
graph,
|
|
1395
|
+
unscaledPoints,
|
|
1396
|
+
options,
|
|
1397
|
+
);
|
|
1398
|
+
arrowHead.attr(
|
|
1399
|
+
_.extend(
|
|
1400
|
+
{
|
|
1401
|
+
"stroke-linejoin": "round",
|
|
1402
|
+
"stroke-linecap": "round",
|
|
1403
|
+
"stroke-dasharray": "",
|
|
1404
|
+
},
|
|
1405
|
+
style,
|
|
1406
|
+
),
|
|
1407
|
+
);
|
|
1408
|
+
|
|
1409
|
+
// Add custom function for transforming arrowheads that
|
|
1410
|
+
// accounts for center, scaling, etc.
|
|
1411
|
+
arrowHead.toCoordAtAngle = function (
|
|
1412
|
+
coord: Coord,
|
|
1413
|
+
angle: number,
|
|
1414
|
+
): void {
|
|
1415
|
+
const clipPoint = graph.scalePoint(
|
|
1416
|
+
getClipPoint(graph, coord, angle),
|
|
1417
|
+
);
|
|
1418
|
+
const do3dTransform = getCanUse3dTransform();
|
|
1419
|
+
arrowHead.transform(
|
|
1420
|
+
"translateX(" +
|
|
1421
|
+
(clipPoint[0] + scale * center[0]) +
|
|
1422
|
+
"px) " +
|
|
1423
|
+
"translateY(" +
|
|
1424
|
+
(clipPoint[1] + scale * center[1]) +
|
|
1425
|
+
"px) " +
|
|
1426
|
+
(do3dTransform ? "translateZ(0) " : "") +
|
|
1427
|
+
"rotate(" +
|
|
1428
|
+
(360 - KhanMath.bound(angle)) +
|
|
1429
|
+
"deg)",
|
|
1430
|
+
);
|
|
1431
|
+
};
|
|
1432
|
+
|
|
1433
|
+
return arrowHead;
|
|
1434
|
+
};
|
|
1435
|
+
|
|
1436
|
+
// Add arrows
|
|
1437
|
+
if (this._arrows == null) {
|
|
1438
|
+
this._arrows = [];
|
|
1439
|
+
|
|
1440
|
+
if (this.extendLine) {
|
|
1441
|
+
this._arrows.push(createArrow(graph, this.normalStyle));
|
|
1442
|
+
this._arrows.push(createArrow(graph, this.normalStyle));
|
|
1443
|
+
} else if (this.extendRay) {
|
|
1444
|
+
this._arrows.push(createArrow(graph, this.normalStyle));
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
const coordForArrow = [this.coordA, this.coordZ];
|
|
1449
|
+
const angleForArrow = [360 - angle, (540 - angle) % 360];
|
|
1450
|
+
_.each(this._arrows, function (arrow, i) {
|
|
1451
|
+
arrow.toCoordAtAngle(coordForArrow[i], angleForArrow[i]);
|
|
1452
|
+
});
|
|
1453
|
+
|
|
1454
|
+
// Temporary objects: array of SVG nodes that get recreated on drag
|
|
1455
|
+
_.invoke(this.temp, "remove");
|
|
1456
|
+
this.temp = [];
|
|
1457
|
+
|
|
1458
|
+
const isClockwise =
|
|
1459
|
+
this.coordA[0] < this.coordZ[0] ||
|
|
1460
|
+
(this.coordA[0] === this.coordZ[0] &&
|
|
1461
|
+
this.coordA[1] > this.coordZ[1]);
|
|
1462
|
+
|
|
1463
|
+
// Update side label
|
|
1464
|
+
if (this.sideLabel) {
|
|
1465
|
+
this.temp.push(
|
|
1466
|
+
this.graph.labelSide({
|
|
1467
|
+
point1: this.coordA,
|
|
1468
|
+
point2: this.coordZ,
|
|
1469
|
+
label: this.labeledSide,
|
|
1470
|
+
text: this.sideLabel,
|
|
1471
|
+
numArrows: this.numArrows,
|
|
1472
|
+
numTicks: this.numTicks,
|
|
1473
|
+
clockwise: isClockwise,
|
|
1474
|
+
style: this.labelStyle,
|
|
1475
|
+
}),
|
|
1476
|
+
);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
// Update vertex labels
|
|
1480
|
+
if (this.vertexLabels.length) {
|
|
1481
|
+
this.graph.labelVertex({
|
|
1482
|
+
vertex: this.coordA,
|
|
1483
|
+
point3: this.coordZ,
|
|
1484
|
+
label: this.labeledVertices[0],
|
|
1485
|
+
text: this.vertexLabels[0],
|
|
1486
|
+
clockwise: isClockwise,
|
|
1487
|
+
style: this.labelStyle,
|
|
1488
|
+
});
|
|
1489
|
+
|
|
1490
|
+
this.graph.labelVertex({
|
|
1491
|
+
point1: this.coordA,
|
|
1492
|
+
vertex: this.coordZ,
|
|
1493
|
+
label: this.labeledVertices[1],
|
|
1494
|
+
text: this.vertexLabels[1],
|
|
1495
|
+
clockwise: isClockwise,
|
|
1496
|
+
style: this.labelStyle,
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
this.temp = _.flatten(this.temp);
|
|
1501
|
+
};
|
|
1502
|
+
|
|
1503
|
+
// Change z-order to back;
|
|
1504
|
+
lineSegment.toBack = function () {
|
|
1505
|
+
if (!lineSegment.fixed) {
|
|
1506
|
+
lineSegment.mouseTarget.toBack();
|
|
1507
|
+
}
|
|
1508
|
+
lineSegment.visibleLine.toBack();
|
|
1509
|
+
};
|
|
1510
|
+
|
|
1511
|
+
// Change z-order to front
|
|
1512
|
+
lineSegment.toFront = function () {
|
|
1513
|
+
if (!lineSegment.fixed) {
|
|
1514
|
+
lineSegment.mouseTarget.toFront();
|
|
1515
|
+
}
|
|
1516
|
+
lineSegment.visibleLine.toFront();
|
|
1517
|
+
};
|
|
1518
|
+
|
|
1519
|
+
lineSegment.remove = function () {
|
|
1520
|
+
if (!lineSegment.fixed) {
|
|
1521
|
+
lineSegment.mouseTarget.remove();
|
|
1522
|
+
}
|
|
1523
|
+
lineSegment.visibleLine.remove();
|
|
1524
|
+
if (lineSegment.labeledSide) {
|
|
1525
|
+
lineSegment.labeledSide.remove();
|
|
1526
|
+
}
|
|
1527
|
+
if (lineSegment.labeledVertices) {
|
|
1528
|
+
_.invoke(lineSegment.labeledVertices, "remove");
|
|
1529
|
+
}
|
|
1530
|
+
if (lineSegment._arrows) {
|
|
1531
|
+
_.invoke(lineSegment._arrows, "remove");
|
|
1532
|
+
}
|
|
1533
|
+
if (lineSegment.temp.length) {
|
|
1534
|
+
_.invoke(lineSegment.temp, "remove");
|
|
1535
|
+
}
|
|
1536
|
+
};
|
|
1537
|
+
|
|
1538
|
+
lineSegment.hide = function () {
|
|
1539
|
+
lineSegment.visibleLine.hide();
|
|
1540
|
+
if (lineSegment.temp.length) {
|
|
1541
|
+
_.invoke(lineSegment.temp, "hide");
|
|
1542
|
+
}
|
|
1543
|
+
if (lineSegment._arrows) {
|
|
1544
|
+
_.invoke(lineSegment._arrows, "hide");
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
|
|
1548
|
+
lineSegment.show = function () {
|
|
1549
|
+
lineSegment.visibleLine.show();
|
|
1550
|
+
if (lineSegment.temp.length) {
|
|
1551
|
+
_.invoke(lineSegment.temp, "show");
|
|
1552
|
+
}
|
|
1553
|
+
if (lineSegment._arrows) {
|
|
1554
|
+
_.invoke(lineSegment._arrows, "show");
|
|
1555
|
+
}
|
|
1556
|
+
};
|
|
1557
|
+
|
|
1558
|
+
if (lineSegment.sideLabel) {
|
|
1559
|
+
lineSegment.labeledSide = this.label(
|
|
1560
|
+
[0, 0],
|
|
1561
|
+
"",
|
|
1562
|
+
"center",
|
|
1563
|
+
lineSegment.labelStyle,
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
if (lineSegment.vertexLabels.length) {
|
|
1568
|
+
lineSegment.labeledVertices = _.map(
|
|
1569
|
+
lineSegment.vertexLabels,
|
|
1570
|
+
function (label) {
|
|
1571
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
1572
|
+
return this.label(
|
|
1573
|
+
[0, 0],
|
|
1574
|
+
"",
|
|
1575
|
+
"center",
|
|
1576
|
+
lineSegment.labelStyle,
|
|
1577
|
+
);
|
|
1578
|
+
},
|
|
1579
|
+
this,
|
|
1580
|
+
);
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
if (!lineSegment.fixed && !lineSegment.constraints.fixed) {
|
|
1584
|
+
const $mouseTarget = $(lineSegment.mouseTarget.getMouseTarget());
|
|
1585
|
+
$mouseTarget.css("cursor", "move");
|
|
1586
|
+
$mouseTarget.bind(
|
|
1587
|
+
"vmousedown vmouseover vmouseout",
|
|
1588
|
+
function (event) {
|
|
1589
|
+
if (event.type === "vmouseover") {
|
|
1590
|
+
if (!dragging) {
|
|
1591
|
+
lineSegment.highlight = true;
|
|
1592
|
+
lineSegment.visibleLine.animate(
|
|
1593
|
+
lineSegment.highlightStyle,
|
|
1594
|
+
50,
|
|
1595
|
+
);
|
|
1596
|
+
lineSegment.arrowStyle = _.extend(
|
|
1597
|
+
{},
|
|
1598
|
+
lineSegment.arrowStyle,
|
|
1599
|
+
{
|
|
1600
|
+
color: lineSegment.highlightStyle.stroke,
|
|
1601
|
+
stroke: lineSegment.highlightStyle.stroke,
|
|
1602
|
+
},
|
|
1603
|
+
);
|
|
1604
|
+
lineSegment.transform();
|
|
1605
|
+
}
|
|
1606
|
+
} else if (event.type === "vmouseout") {
|
|
1607
|
+
lineSegment.highlight = false;
|
|
1608
|
+
if (!lineSegment.dragging) {
|
|
1609
|
+
lineSegment.visibleLine.animate(
|
|
1610
|
+
lineSegment.normalStyle,
|
|
1611
|
+
50,
|
|
1612
|
+
);
|
|
1613
|
+
lineSegment.arrowStyle = _.extend(
|
|
1614
|
+
{},
|
|
1615
|
+
lineSegment.arrowStyle,
|
|
1616
|
+
{
|
|
1617
|
+
color: lineSegment.normalStyle.stroke,
|
|
1618
|
+
stroke: lineSegment.normalStyle.stroke,
|
|
1619
|
+
},
|
|
1620
|
+
);
|
|
1621
|
+
lineSegment.transform();
|
|
1622
|
+
}
|
|
1623
|
+
} else if (
|
|
1624
|
+
event.type === "vmousedown" &&
|
|
1625
|
+
(event.which === 1 || event.which === 0)
|
|
1626
|
+
) {
|
|
1627
|
+
event.preventDefault();
|
|
1628
|
+
let coordX =
|
|
1629
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
1630
|
+
(event.pageX -
|
|
1631
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
1632
|
+
$(graph.raphael.canvas.parentNode).offset()
|
|
1633
|
+
.left) /
|
|
1634
|
+
graph.scale[0] +
|
|
1635
|
+
graph.range[0][0];
|
|
1636
|
+
let coordY =
|
|
1637
|
+
graph.range[1][1] -
|
|
1638
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
1639
|
+
(event.pageY -
|
|
1640
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
1641
|
+
$(graph.raphael.canvas.parentNode).offset()
|
|
1642
|
+
.top) /
|
|
1643
|
+
graph.scale[1];
|
|
1644
|
+
if (lineSegment.snapX > 0) {
|
|
1645
|
+
coordX =
|
|
1646
|
+
Math.round(coordX / lineSegment.snapX) *
|
|
1647
|
+
lineSegment.snapX;
|
|
1648
|
+
}
|
|
1649
|
+
if (lineSegment.snapY > 0) {
|
|
1650
|
+
coordY =
|
|
1651
|
+
Math.round(coordY / lineSegment.snapY) *
|
|
1652
|
+
lineSegment.snapY;
|
|
1653
|
+
}
|
|
1654
|
+
const mouseOffsetA = [
|
|
1655
|
+
lineSegment.coordA[0] - coordX,
|
|
1656
|
+
lineSegment.coordA[1] - coordY,
|
|
1657
|
+
];
|
|
1658
|
+
const mouseOffsetZ = [
|
|
1659
|
+
lineSegment.coordZ[0] - coordX,
|
|
1660
|
+
lineSegment.coordZ[1] - coordY,
|
|
1661
|
+
];
|
|
1662
|
+
|
|
1663
|
+
const offsetLeft = -Math.min(
|
|
1664
|
+
graph.scaleVector(mouseOffsetA)[0],
|
|
1665
|
+
graph.scaleVector(mouseOffsetZ)[0],
|
|
1666
|
+
);
|
|
1667
|
+
const offsetRight = Math.max(
|
|
1668
|
+
graph.scaleVector(mouseOffsetA)[0],
|
|
1669
|
+
graph.scaleVector(mouseOffsetZ)[0],
|
|
1670
|
+
);
|
|
1671
|
+
const offsetTop = Math.max(
|
|
1672
|
+
graph.scaleVector(mouseOffsetA)[1],
|
|
1673
|
+
graph.scaleVector(mouseOffsetZ)[1],
|
|
1674
|
+
);
|
|
1675
|
+
const offsetBottom = -Math.min(
|
|
1676
|
+
graph.scaleVector(mouseOffsetA)[1],
|
|
1677
|
+
graph.scaleVector(mouseOffsetZ)[1],
|
|
1678
|
+
);
|
|
1679
|
+
|
|
1680
|
+
$(document).bind(
|
|
1681
|
+
"vmousemove.lineSegment vmouseup.lineSegment",
|
|
1682
|
+
function (event) {
|
|
1683
|
+
event.preventDefault();
|
|
1684
|
+
lineSegment.dragging = true;
|
|
1685
|
+
dragging = true;
|
|
1686
|
+
|
|
1687
|
+
let mouseX =
|
|
1688
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
1689
|
+
event.pageX -
|
|
1690
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
1691
|
+
$(graph.raphael.canvas.parentNode).offset()
|
|
1692
|
+
.left;
|
|
1693
|
+
let mouseY =
|
|
1694
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
1695
|
+
event.pageY -
|
|
1696
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
1697
|
+
$(graph.raphael.canvas.parentNode).offset()
|
|
1698
|
+
.top;
|
|
1699
|
+
// no part of the line segment can go beyond 10
|
|
1700
|
+
// pixels from the edge
|
|
1701
|
+
mouseX = Math.max(
|
|
1702
|
+
offsetLeft + 10,
|
|
1703
|
+
Math.min(
|
|
1704
|
+
graph.xpixels - 10 - offsetRight,
|
|
1705
|
+
mouseX,
|
|
1706
|
+
),
|
|
1707
|
+
);
|
|
1708
|
+
mouseY = Math.max(
|
|
1709
|
+
offsetTop + 10,
|
|
1710
|
+
Math.min(
|
|
1711
|
+
graph.ypixels - 10 - offsetBottom,
|
|
1712
|
+
mouseY,
|
|
1713
|
+
),
|
|
1714
|
+
);
|
|
1715
|
+
|
|
1716
|
+
let coordX =
|
|
1717
|
+
mouseX / graph.scale[0] + graph.range[0][0];
|
|
1718
|
+
let coordY =
|
|
1719
|
+
graph.range[1][1] - mouseY / graph.scale[1];
|
|
1720
|
+
if (lineSegment.snapX > 0) {
|
|
1721
|
+
coordX =
|
|
1722
|
+
Math.round(coordX / lineSegment.snapX) *
|
|
1723
|
+
lineSegment.snapX;
|
|
1724
|
+
}
|
|
1725
|
+
if (lineSegment.snapY > 0) {
|
|
1726
|
+
coordY =
|
|
1727
|
+
Math.round(coordY / lineSegment.snapY) *
|
|
1728
|
+
lineSegment.snapY;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
if (event.type === "vmousemove") {
|
|
1732
|
+
if (lineSegment.constraints.constrainX) {
|
|
1733
|
+
coordX =
|
|
1734
|
+
lineSegment.coordA[0] -
|
|
1735
|
+
mouseOffsetA[0];
|
|
1736
|
+
}
|
|
1737
|
+
if (lineSegment.constraints.constrainY) {
|
|
1738
|
+
coordY =
|
|
1739
|
+
lineSegment.coordA[1] -
|
|
1740
|
+
mouseOffsetA[1];
|
|
1741
|
+
}
|
|
1742
|
+
const dX =
|
|
1743
|
+
coordX +
|
|
1744
|
+
mouseOffsetA[0] -
|
|
1745
|
+
lineSegment.coordA[0];
|
|
1746
|
+
const dY =
|
|
1747
|
+
coordY +
|
|
1748
|
+
mouseOffsetA[1] -
|
|
1749
|
+
lineSegment.coordA[1];
|
|
1750
|
+
lineSegment.coordA = [
|
|
1751
|
+
coordX + mouseOffsetA[0],
|
|
1752
|
+
coordY + mouseOffsetA[1],
|
|
1753
|
+
];
|
|
1754
|
+
lineSegment.coordZ = [
|
|
1755
|
+
coordX + mouseOffsetZ[0],
|
|
1756
|
+
coordY + mouseOffsetZ[1],
|
|
1757
|
+
];
|
|
1758
|
+
lineSegment.transform();
|
|
1759
|
+
|
|
1760
|
+
if (lineSegment.movePointsWithLine) {
|
|
1761
|
+
// If the points are movablePoints, adjust
|
|
1762
|
+
// their coordinates when the line itself is
|
|
1763
|
+
// dragged
|
|
1764
|
+
if (
|
|
1765
|
+
typeof lineSegment.pointA ===
|
|
1766
|
+
"object"
|
|
1767
|
+
) {
|
|
1768
|
+
lineSegment.pointA.setCoord([
|
|
1769
|
+
lineSegment.pointA.coord[0] +
|
|
1770
|
+
dX,
|
|
1771
|
+
lineSegment.pointA.coord[1] +
|
|
1772
|
+
dY,
|
|
1773
|
+
]);
|
|
1774
|
+
}
|
|
1775
|
+
if (
|
|
1776
|
+
typeof lineSegment.pointZ ===
|
|
1777
|
+
"object"
|
|
1778
|
+
) {
|
|
1779
|
+
lineSegment.pointZ.setCoord([
|
|
1780
|
+
lineSegment.pointZ.coord[0] +
|
|
1781
|
+
dX,
|
|
1782
|
+
lineSegment.pointZ.coord[1] +
|
|
1783
|
+
dY,
|
|
1784
|
+
]);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
if (_.isFunction(lineSegment.onMove)) {
|
|
1789
|
+
lineSegment.onMove(dX, dY);
|
|
1790
|
+
}
|
|
1791
|
+
} else if (event.type === "vmouseup") {
|
|
1792
|
+
$(document).unbind(".lineSegment");
|
|
1793
|
+
lineSegment.dragging = false;
|
|
1794
|
+
dragging = false;
|
|
1795
|
+
if (!lineSegment.highlight) {
|
|
1796
|
+
lineSegment.visibleLine.animate(
|
|
1797
|
+
lineSegment.normalStyle,
|
|
1798
|
+
50,
|
|
1799
|
+
);
|
|
1800
|
+
lineSegment.arrowStyle = _.extend(
|
|
1801
|
+
{},
|
|
1802
|
+
lineSegment.arrowStyle,
|
|
1803
|
+
{
|
|
1804
|
+
color: lineSegment.normalStyle
|
|
1805
|
+
.stroke,
|
|
1806
|
+
stroke: lineSegment.normalStyle
|
|
1807
|
+
.stroke,
|
|
1808
|
+
},
|
|
1809
|
+
);
|
|
1810
|
+
lineSegment.transform();
|
|
1811
|
+
}
|
|
1812
|
+
if (_.isFunction(lineSegment.onMoveEnd)) {
|
|
1813
|
+
lineSegment.onMoveEnd();
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
$(lineSegment).trigger("move");
|
|
1818
|
+
},
|
|
1819
|
+
);
|
|
1820
|
+
}
|
|
1821
|
+
},
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
if (lineSegment.pointA !== undefined) {
|
|
1826
|
+
lineSegment.pointA.toFront();
|
|
1827
|
+
}
|
|
1828
|
+
if (lineSegment.pointZ !== undefined) {
|
|
1829
|
+
lineSegment.pointZ.toFront();
|
|
1830
|
+
}
|
|
1831
|
+
lineSegment.transform();
|
|
1832
|
+
return lineSegment;
|
|
1833
|
+
},
|
|
1834
|
+
|
|
1835
|
+
// MovablePolygon is a polygon that can be dragged around the screen.
|
|
1836
|
+
// By attaching a smartPoint to each vertex, the points can be
|
|
1837
|
+
// manipulated individually.
|
|
1838
|
+
//
|
|
1839
|
+
// To use with smartPoints, add the smartPoints first, then:
|
|
1840
|
+
// addMovablePolygon({points: [...]});
|
|
1841
|
+
//
|
|
1842
|
+
// Include "fixed: true" in the options if you don't want the entire
|
|
1843
|
+
// polygon to be draggable (you can still use points to make the
|
|
1844
|
+
// vertices draggable)
|
|
1845
|
+
//
|
|
1846
|
+
// The returned object includes the following properties/methods:
|
|
1847
|
+
//
|
|
1848
|
+
// - polygon.points
|
|
1849
|
+
// The polygon's dynamic smartPoints and static coordinates, mixed.
|
|
1850
|
+
//
|
|
1851
|
+
// - polygon.coords
|
|
1852
|
+
// The polygon's current coordinates (generated, don't edit).
|
|
1853
|
+
//
|
|
1854
|
+
// - polygon.transform()
|
|
1855
|
+
// Repositions the polygon. Call after changing any points.
|
|
1856
|
+
//
|
|
1857
|
+
addMovablePolygon: function (options) {
|
|
1858
|
+
const graphie = this;
|
|
1859
|
+
|
|
1860
|
+
const polygon = $.extend(
|
|
1861
|
+
{
|
|
1862
|
+
snapX: 0,
|
|
1863
|
+
snapY: 0,
|
|
1864
|
+
fixed: false,
|
|
1865
|
+
constrainToGraph: true,
|
|
1866
|
+
normalStyle: {},
|
|
1867
|
+
highlightStyle: {
|
|
1868
|
+
stroke: KhanColors.INTERACTING,
|
|
1869
|
+
"stroke-width": 2,
|
|
1870
|
+
fill: KhanColors.INTERACTING,
|
|
1871
|
+
"fill-opacity": 0.05,
|
|
1872
|
+
},
|
|
1873
|
+
pointHighlightStyle: {
|
|
1874
|
+
fill: KhanColors.INTERACTING,
|
|
1875
|
+
stroke: KhanColors.INTERACTING,
|
|
1876
|
+
},
|
|
1877
|
+
labelStyle: {
|
|
1878
|
+
stroke: KhanColors.DYNAMIC,
|
|
1879
|
+
"stroke-width": 1,
|
|
1880
|
+
color: KhanColors.DYNAMIC,
|
|
1881
|
+
},
|
|
1882
|
+
angleLabels: [],
|
|
1883
|
+
showRightAngleMarkers: [],
|
|
1884
|
+
sideLabels: [],
|
|
1885
|
+
vertexLabels: [],
|
|
1886
|
+
numArcs: [],
|
|
1887
|
+
numArrows: [],
|
|
1888
|
+
numTicks: [],
|
|
1889
|
+
updateOnPointMove: true,
|
|
1890
|
+
closed: true,
|
|
1891
|
+
},
|
|
1892
|
+
_.omit(options, "points"),
|
|
1893
|
+
);
|
|
1894
|
+
|
|
1895
|
+
const normalColor = polygon.fixed
|
|
1896
|
+
? KhanColors.DYNAMIC
|
|
1897
|
+
: KhanColors.INTERACTIVE;
|
|
1898
|
+
polygon.normalStyle = _.extend(
|
|
1899
|
+
{
|
|
1900
|
+
"stroke-width": 2,
|
|
1901
|
+
"fill-opacity": 0,
|
|
1902
|
+
fill: normalColor,
|
|
1903
|
+
stroke: normalColor,
|
|
1904
|
+
},
|
|
1905
|
+
options.normalStyle,
|
|
1906
|
+
);
|
|
1907
|
+
|
|
1908
|
+
// don't deep copy the points array with $.extend;
|
|
1909
|
+
// we may want to append to it later for click-to-add-points
|
|
1910
|
+
polygon.points = options.points;
|
|
1911
|
+
|
|
1912
|
+
const isPoint = function (coordOrPoint: any) {
|
|
1913
|
+
return !_.isArray(coordOrPoint);
|
|
1914
|
+
};
|
|
1915
|
+
|
|
1916
|
+
polygon.update = function () {
|
|
1917
|
+
const n = polygon.points.length;
|
|
1918
|
+
|
|
1919
|
+
// Update coords
|
|
1920
|
+
polygon.coords = _.map(polygon.points, function (coordOrPoint, i) {
|
|
1921
|
+
if (isPoint(coordOrPoint)) {
|
|
1922
|
+
return coordOrPoint.coord;
|
|
1923
|
+
}
|
|
1924
|
+
return coordOrPoint;
|
|
1925
|
+
});
|
|
1926
|
+
|
|
1927
|
+
// Calculate bounding box
|
|
1928
|
+
polygon.left = _.min(_.pluck(polygon.coords, 0));
|
|
1929
|
+
polygon.right = _.max(_.pluck(polygon.coords, 0));
|
|
1930
|
+
polygon.top = _.max(_.pluck(polygon.coords, 1));
|
|
1931
|
+
polygon.bottom = _.min(_.pluck(polygon.coords, 1));
|
|
1932
|
+
|
|
1933
|
+
let scaledCoords = _.map(polygon.coords, function (coord) {
|
|
1934
|
+
return graphie.scalePoint(coord);
|
|
1935
|
+
});
|
|
1936
|
+
|
|
1937
|
+
// Create path
|
|
1938
|
+
if (polygon.closed) {
|
|
1939
|
+
scaledCoords.push(true);
|
|
1940
|
+
} else {
|
|
1941
|
+
// For open polygons, concatenate a reverse of the path,
|
|
1942
|
+
// to remove the inside area of the path, which would
|
|
1943
|
+
// otherwise be clickable (even if the closing line segment
|
|
1944
|
+
// wasn't drawn
|
|
1945
|
+
scaledCoords = scaledCoords.concat(
|
|
1946
|
+
_.clone(scaledCoords).reverse(),
|
|
1947
|
+
);
|
|
1948
|
+
}
|
|
1949
|
+
polygon.path = GraphUtils.unscaledSvgPath(scaledCoords);
|
|
1950
|
+
|
|
1951
|
+
// Temporary objects
|
|
1952
|
+
_.invoke(polygon.temp, "remove");
|
|
1953
|
+
polygon.temp = [];
|
|
1954
|
+
|
|
1955
|
+
const isClockwise = clockwise(polygon.coords);
|
|
1956
|
+
|
|
1957
|
+
// Update angle labels
|
|
1958
|
+
if (
|
|
1959
|
+
polygon.angleLabels.length ||
|
|
1960
|
+
polygon.showRightAngleMarkers.length
|
|
1961
|
+
) {
|
|
1962
|
+
_.each(polygon.labeledAngles, function (label, i) {
|
|
1963
|
+
polygon.temp.push(
|
|
1964
|
+
graphie.labelAngle({
|
|
1965
|
+
point1: polygon.coords[(i - 1 + n) % n],
|
|
1966
|
+
vertex: polygon.coords[i],
|
|
1967
|
+
point3: polygon.coords[(i + 1) % n],
|
|
1968
|
+
label: label,
|
|
1969
|
+
text: polygon.angleLabels[i],
|
|
1970
|
+
showRightAngleMarker:
|
|
1971
|
+
polygon.showRightAngleMarkers[i],
|
|
1972
|
+
numArcs: polygon.numArcs[i],
|
|
1973
|
+
clockwise: isClockwise,
|
|
1974
|
+
style: polygon.labelStyle,
|
|
1975
|
+
}),
|
|
1976
|
+
);
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
// Update side labels
|
|
1981
|
+
if (polygon.sideLabels.length) {
|
|
1982
|
+
_.each(polygon.labeledSides, function (label, i) {
|
|
1983
|
+
polygon.temp.push(
|
|
1984
|
+
graphie.labelSide({
|
|
1985
|
+
point1: polygon.coords[i],
|
|
1986
|
+
point2: polygon.coords[(i + 1) % n],
|
|
1987
|
+
label: label,
|
|
1988
|
+
text: polygon.sideLabels[i],
|
|
1989
|
+
numArrows: polygon.numArrows[i],
|
|
1990
|
+
numTicks: polygon.numTicks[i],
|
|
1991
|
+
clockwise: isClockwise,
|
|
1992
|
+
style: polygon.labelStyle,
|
|
1993
|
+
}),
|
|
1994
|
+
);
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
// Update vertex labels
|
|
1999
|
+
if (polygon.vertexLabels.length) {
|
|
2000
|
+
_.each(polygon.labeledVertices, function (label, i) {
|
|
2001
|
+
graphie.labelVertex({
|
|
2002
|
+
point1: polygon.coords[(i - 1 + n) % n],
|
|
2003
|
+
vertex: polygon.coords[i],
|
|
2004
|
+
point3: polygon.coords[(i + 1) % n],
|
|
2005
|
+
label: label,
|
|
2006
|
+
text: polygon.vertexLabels[i],
|
|
2007
|
+
clockwise: isClockwise,
|
|
2008
|
+
style: polygon.labelStyle,
|
|
2009
|
+
});
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
polygon.temp = _.flatten(polygon.temp);
|
|
2014
|
+
};
|
|
2015
|
+
|
|
2016
|
+
polygon.transform = function () {
|
|
2017
|
+
polygon.update();
|
|
2018
|
+
|
|
2019
|
+
polygon.visibleShape.attr({path: polygon.path});
|
|
2020
|
+
|
|
2021
|
+
if (!polygon.fixed) {
|
|
2022
|
+
polygon.mouseTarget.attr({path: polygon.path});
|
|
2023
|
+
}
|
|
2024
|
+
};
|
|
2025
|
+
|
|
2026
|
+
polygon.remove = function () {
|
|
2027
|
+
polygon.visibleShape.remove();
|
|
2028
|
+
|
|
2029
|
+
if (!polygon.fixed) {
|
|
2030
|
+
polygon.mouseTarget.remove();
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
if (polygon.labeledAngles) {
|
|
2034
|
+
_.invoke(polygon.labeledAngles, "remove");
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
if (polygon.labeledSides) {
|
|
2038
|
+
_.invoke(polygon.labeledSides, "remove");
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
if (polygon.labeledVertices) {
|
|
2042
|
+
_.invoke(polygon.labeledVertices, "remove");
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
if (polygon.temp.length) {
|
|
2046
|
+
_.invoke(polygon.temp, "remove");
|
|
2047
|
+
}
|
|
2048
|
+
};
|
|
2049
|
+
|
|
2050
|
+
polygon.toBack = function () {
|
|
2051
|
+
if (!polygon.fixed) {
|
|
2052
|
+
polygon.mouseTarget.toBack();
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
polygon.visibleShape.toBack();
|
|
2056
|
+
};
|
|
2057
|
+
|
|
2058
|
+
polygon.toFront = function () {
|
|
2059
|
+
if (!polygon.fixed) {
|
|
2060
|
+
polygon.mouseTarget.toFront();
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
polygon.visibleShape.toFront();
|
|
2064
|
+
};
|
|
2065
|
+
|
|
2066
|
+
// Setup
|
|
2067
|
+
|
|
2068
|
+
if (polygon.updateOnPointMove) {
|
|
2069
|
+
_.each(_.filter(polygon.points, isPoint), function (coordOrPoint) {
|
|
2070
|
+
coordOrPoint.polygonVertices.push(polygon);
|
|
2071
|
+
});
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
polygon.coords = new Array(polygon.points.length);
|
|
2075
|
+
|
|
2076
|
+
if (polygon.angleLabels.length) {
|
|
2077
|
+
const numLabels = Math.max(
|
|
2078
|
+
polygon.angleLabels.length,
|
|
2079
|
+
polygon.showRightAngleMarkers.length,
|
|
2080
|
+
);
|
|
2081
|
+
polygon.labeledAngles = _.times(
|
|
2082
|
+
numLabels,
|
|
2083
|
+
function () {
|
|
2084
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
2085
|
+
return this.label([0, 0], "", "center", polygon.labelStyle);
|
|
2086
|
+
},
|
|
2087
|
+
this,
|
|
2088
|
+
);
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
if (polygon.sideLabels.length) {
|
|
2092
|
+
polygon.labeledSides = _.map(
|
|
2093
|
+
polygon.sideLabels,
|
|
2094
|
+
function (label) {
|
|
2095
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
2096
|
+
return this.label([0, 0], "", "center", polygon.labelStyle);
|
|
2097
|
+
},
|
|
2098
|
+
this,
|
|
2099
|
+
);
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
if (polygon.vertexLabels.length) {
|
|
2103
|
+
polygon.labeledVertices = _.map(
|
|
2104
|
+
polygon.vertexLabels,
|
|
2105
|
+
function (label) {
|
|
2106
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
2107
|
+
return this.label([0, 0], "", "center", polygon.labelStyle);
|
|
2108
|
+
},
|
|
2109
|
+
this,
|
|
2110
|
+
);
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
polygon.update();
|
|
2114
|
+
|
|
2115
|
+
polygon.visibleShape = graphie.raphael.path(polygon.path);
|
|
2116
|
+
polygon.visibleShape.attr(polygon.normalStyle);
|
|
2117
|
+
|
|
2118
|
+
if (!polygon.fixed) {
|
|
2119
|
+
polygon.mouseTarget = graphie.mouselayer.path(polygon.path);
|
|
2120
|
+
polygon.mouseTarget.attr({
|
|
2121
|
+
fill: "#000",
|
|
2122
|
+
opacity: 0,
|
|
2123
|
+
cursor: "move",
|
|
2124
|
+
});
|
|
2125
|
+
|
|
2126
|
+
$(polygon.mouseTarget[0]).bind(
|
|
2127
|
+
"vmousedown vmouseover vmouseout",
|
|
2128
|
+
function (event) {
|
|
2129
|
+
if (event.type === "vmouseover") {
|
|
2130
|
+
if (!dragging || polygon.dragging) {
|
|
2131
|
+
polygon.highlight = true;
|
|
2132
|
+
polygon.visibleShape.animate(
|
|
2133
|
+
polygon.highlightStyle,
|
|
2134
|
+
50,
|
|
2135
|
+
);
|
|
2136
|
+
_.each(
|
|
2137
|
+
_.filter(polygon.points, isPoint),
|
|
2138
|
+
function (point) {
|
|
2139
|
+
point.visibleShape.animate(
|
|
2140
|
+
polygon.pointHighlightStyle,
|
|
2141
|
+
50,
|
|
2142
|
+
);
|
|
2143
|
+
},
|
|
2144
|
+
);
|
|
2145
|
+
}
|
|
2146
|
+
} else if (event.type === "vmouseout") {
|
|
2147
|
+
polygon.highlight = false;
|
|
2148
|
+
if (!polygon.dragging) {
|
|
2149
|
+
polygon.visibleShape.animate(
|
|
2150
|
+
polygon.normalStyle,
|
|
2151
|
+
50,
|
|
2152
|
+
);
|
|
2153
|
+
const points = _.filter(polygon.points, isPoint);
|
|
2154
|
+
if (!_.any(_.pluck(points, "dragging"))) {
|
|
2155
|
+
_.each(points, function (point) {
|
|
2156
|
+
point.visibleShape.animate(
|
|
2157
|
+
point.normalStyle,
|
|
2158
|
+
50,
|
|
2159
|
+
);
|
|
2160
|
+
});
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
} else if (
|
|
2164
|
+
event.type === "vmousedown" &&
|
|
2165
|
+
(event.which === 1 || event.which === 0)
|
|
2166
|
+
) {
|
|
2167
|
+
event.preventDefault();
|
|
2168
|
+
|
|
2169
|
+
_.each(
|
|
2170
|
+
_.filter(polygon.points, isPoint),
|
|
2171
|
+
function (point) {
|
|
2172
|
+
point.dragging = true;
|
|
2173
|
+
},
|
|
2174
|
+
);
|
|
2175
|
+
|
|
2176
|
+
let startX =
|
|
2177
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
2178
|
+
(event.pageX -
|
|
2179
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
2180
|
+
$(graphie.raphael.canvas.parentNode).offset()
|
|
2181
|
+
.left) /
|
|
2182
|
+
graphie.scale[0] +
|
|
2183
|
+
graphie.range[0][0];
|
|
2184
|
+
let startY =
|
|
2185
|
+
graphie.range[1][1] -
|
|
2186
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
2187
|
+
(event.pageY -
|
|
2188
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
2189
|
+
$(graphie.raphael.canvas.parentNode).offset()
|
|
2190
|
+
.top) /
|
|
2191
|
+
graphie.scale[1];
|
|
2192
|
+
if (polygon.snapX > 0) {
|
|
2193
|
+
startX =
|
|
2194
|
+
Math.round(startX / polygon.snapX) *
|
|
2195
|
+
polygon.snapX;
|
|
2196
|
+
}
|
|
2197
|
+
if (polygon.snapY > 0) {
|
|
2198
|
+
startY =
|
|
2199
|
+
Math.round(startY / polygon.snapY) *
|
|
2200
|
+
polygon.snapY;
|
|
2201
|
+
}
|
|
2202
|
+
let lastX = startX;
|
|
2203
|
+
let lastY = startY;
|
|
2204
|
+
|
|
2205
|
+
const polygonCoords = polygon.coords.slice();
|
|
2206
|
+
|
|
2207
|
+
const offsetLeft =
|
|
2208
|
+
(startX - polygon.left) * graphie.scale[0];
|
|
2209
|
+
const offsetRight =
|
|
2210
|
+
(polygon.right - startX) * graphie.scale[0];
|
|
2211
|
+
const offsetTop =
|
|
2212
|
+
(polygon.top - startY) * graphie.scale[1];
|
|
2213
|
+
const offsetBottom =
|
|
2214
|
+
(startY - polygon.bottom) * graphie.scale[1];
|
|
2215
|
+
|
|
2216
|
+
$(document).bind(
|
|
2217
|
+
"vmousemove.polygon vmouseup.polygon",
|
|
2218
|
+
function (event) {
|
|
2219
|
+
event.preventDefault();
|
|
2220
|
+
|
|
2221
|
+
polygon.dragging = true;
|
|
2222
|
+
dragging = true;
|
|
2223
|
+
|
|
2224
|
+
let mouseX =
|
|
2225
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
2226
|
+
event.pageX -
|
|
2227
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
2228
|
+
$(
|
|
2229
|
+
graphie.raphael.canvas.parentNode,
|
|
2230
|
+
).offset().left;
|
|
2231
|
+
let mouseY =
|
|
2232
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
2233
|
+
event.pageY -
|
|
2234
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
2235
|
+
$(
|
|
2236
|
+
graphie.raphael.canvas.parentNode,
|
|
2237
|
+
).offset().top;
|
|
2238
|
+
|
|
2239
|
+
// no part of the polygon can go beyond 10 pixels from
|
|
2240
|
+
// the edge
|
|
2241
|
+
if (polygon.constrainToGraph) {
|
|
2242
|
+
mouseX = Math.max(
|
|
2243
|
+
offsetLeft + 10,
|
|
2244
|
+
Math.min(
|
|
2245
|
+
graphie.xpixels - 10 - offsetRight,
|
|
2246
|
+
mouseX,
|
|
2247
|
+
),
|
|
2248
|
+
);
|
|
2249
|
+
mouseY = Math.max(
|
|
2250
|
+
offsetTop + 10,
|
|
2251
|
+
Math.min(
|
|
2252
|
+
graphie.ypixels - 10 - offsetBottom,
|
|
2253
|
+
mouseY,
|
|
2254
|
+
),
|
|
2255
|
+
);
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
let currentX =
|
|
2259
|
+
mouseX / graphie.scale[0] +
|
|
2260
|
+
graphie.range[0][0];
|
|
2261
|
+
let currentY =
|
|
2262
|
+
graphie.range[1][1] -
|
|
2263
|
+
mouseY / graphie.scale[1];
|
|
2264
|
+
if (polygon.snapX > 0) {
|
|
2265
|
+
currentX =
|
|
2266
|
+
Math.round(currentX / polygon.snapX) *
|
|
2267
|
+
polygon.snapX;
|
|
2268
|
+
}
|
|
2269
|
+
if (polygon.snapY > 0) {
|
|
2270
|
+
currentY =
|
|
2271
|
+
Math.round(currentY / polygon.snapY) *
|
|
2272
|
+
polygon.snapY;
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
if (event.type === "vmousemove") {
|
|
2276
|
+
let dX = currentX - startX;
|
|
2277
|
+
let dY = currentY - startY;
|
|
2278
|
+
|
|
2279
|
+
let doMove = true;
|
|
2280
|
+
if (_.isFunction(polygon.onMove)) {
|
|
2281
|
+
const onMoveResult = polygon.onMove(
|
|
2282
|
+
dX,
|
|
2283
|
+
dY,
|
|
2284
|
+
);
|
|
2285
|
+
if (onMoveResult === false) {
|
|
2286
|
+
doMove = false;
|
|
2287
|
+
} else if (_.isArray(onMoveResult)) {
|
|
2288
|
+
dX = onMoveResult[0];
|
|
2289
|
+
dY = onMoveResult[1];
|
|
2290
|
+
currentX = startX + dX;
|
|
2291
|
+
currentY = startY + dY;
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
const increment = function (i: any) {
|
|
2296
|
+
return [
|
|
2297
|
+
polygonCoords[i][0] + dX,
|
|
2298
|
+
polygonCoords[i][1] + dY,
|
|
2299
|
+
];
|
|
2300
|
+
};
|
|
2301
|
+
|
|
2302
|
+
if (doMove) {
|
|
2303
|
+
_.each(
|
|
2304
|
+
polygon.points,
|
|
2305
|
+
function (coordOrPoint, i) {
|
|
2306
|
+
if (isPoint(coordOrPoint)) {
|
|
2307
|
+
coordOrPoint.setCoord(
|
|
2308
|
+
increment(i),
|
|
2309
|
+
);
|
|
2310
|
+
} else {
|
|
2311
|
+
polygon.points[i] =
|
|
2312
|
+
increment(i);
|
|
2313
|
+
}
|
|
2314
|
+
},
|
|
2315
|
+
);
|
|
2316
|
+
|
|
2317
|
+
polygon.transform();
|
|
2318
|
+
|
|
2319
|
+
$(polygon).trigger("move");
|
|
2320
|
+
|
|
2321
|
+
lastX = currentX;
|
|
2322
|
+
lastY = currentY;
|
|
2323
|
+
}
|
|
2324
|
+
} else if (event.type === "vmouseup") {
|
|
2325
|
+
$(document).unbind(".polygon");
|
|
2326
|
+
|
|
2327
|
+
const points = _.filter(
|
|
2328
|
+
polygon.points,
|
|
2329
|
+
isPoint,
|
|
2330
|
+
);
|
|
2331
|
+
_.each(points, function (point) {
|
|
2332
|
+
point.dragging = false;
|
|
2333
|
+
});
|
|
2334
|
+
|
|
2335
|
+
polygon.dragging = false;
|
|
2336
|
+
dragging = false;
|
|
2337
|
+
if (!polygon.highlight) {
|
|
2338
|
+
polygon.visibleShape.animate(
|
|
2339
|
+
polygon.normalStyle,
|
|
2340
|
+
50,
|
|
2341
|
+
);
|
|
2342
|
+
|
|
2343
|
+
_.each(points, function (point) {
|
|
2344
|
+
point.visibleShape.animate(
|
|
2345
|
+
point.normalStyle,
|
|
2346
|
+
50,
|
|
2347
|
+
);
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
if (_.isFunction(polygon.onMoveEnd)) {
|
|
2351
|
+
polygon.onMoveEnd(
|
|
2352
|
+
lastX - startX,
|
|
2353
|
+
lastY - startY,
|
|
2354
|
+
);
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
},
|
|
2358
|
+
);
|
|
2359
|
+
}
|
|
2360
|
+
},
|
|
2361
|
+
);
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
// Bring any movable points to the front
|
|
2365
|
+
_.invoke(_.filter(polygon.points, isPoint), "toFront");
|
|
2366
|
+
|
|
2367
|
+
return polygon;
|
|
2368
|
+
},
|
|
2369
|
+
|
|
2370
|
+
/**
|
|
2371
|
+
* Constrain a point to be within the graph (including padding).
|
|
2372
|
+
* If outside graph, point's x and y coordinates are clamped within
|
|
2373
|
+
* the graph.
|
|
2374
|
+
*/
|
|
2375
|
+
constrainToBounds: function (point, padding) {
|
|
2376
|
+
const lower = this.unscalePoint([padding, this.ypixels - padding]);
|
|
2377
|
+
const upper = this.unscalePoint([this.xpixels - padding, padding]);
|
|
2378
|
+
const coordX = Math.max(lower[0], Math.min(upper[0], point[0]));
|
|
2379
|
+
const coordY = Math.max(lower[1], Math.min(upper[1], point[1]));
|
|
2380
|
+
return [coordX, coordY];
|
|
2381
|
+
},
|
|
2382
|
+
|
|
2383
|
+
/**
|
|
2384
|
+
* Constrain a point to be within the graph (including padding).
|
|
2385
|
+
* If outside graph, point is moved along the ray specified by angle
|
|
2386
|
+
* until inside graph.
|
|
2387
|
+
*/
|
|
2388
|
+
constrainToBoundsOnAngle: function (point, padding, angle) {
|
|
2389
|
+
const lower = this.unscalePoint([padding, this.ypixels - padding]);
|
|
2390
|
+
const upper = this.unscalePoint([this.xpixels - padding, padding]);
|
|
2391
|
+
|
|
2392
|
+
let result = point.slice();
|
|
2393
|
+
|
|
2394
|
+
if (result[0] < lower[0]) {
|
|
2395
|
+
result = [
|
|
2396
|
+
lower[0],
|
|
2397
|
+
result[1] + (lower[0] - result[0]) * Math.tan(angle),
|
|
2398
|
+
];
|
|
2399
|
+
} else if (result[0] > upper[0]) {
|
|
2400
|
+
result = [
|
|
2401
|
+
upper[0],
|
|
2402
|
+
result[1] - (result[0] - upper[0]) * Math.tan(angle),
|
|
2403
|
+
];
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
if (result[1] < lower[1]) {
|
|
2407
|
+
result = [
|
|
2408
|
+
result[0] + (lower[1] - result[1]) / Math.tan(angle),
|
|
2409
|
+
lower[1],
|
|
2410
|
+
];
|
|
2411
|
+
} else if (result[1] > upper[1]) {
|
|
2412
|
+
result = [
|
|
2413
|
+
result[0] - (result[1] - upper[1]) / Math.tan(angle),
|
|
2414
|
+
upper[1],
|
|
2415
|
+
];
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
return result;
|
|
2419
|
+
},
|
|
2420
|
+
|
|
2421
|
+
// MovableAngle is an angle that can be dragged around the screen.
|
|
2422
|
+
// By attaching a smartPoint to the vertex and ray control points, the
|
|
2423
|
+
// rays can be manipulated individually.
|
|
2424
|
+
//
|
|
2425
|
+
// Use only with smartPoints; add the smartPoints first, then:
|
|
2426
|
+
// addMovableAngle({points: [...]});
|
|
2427
|
+
//
|
|
2428
|
+
// The rays can be controlled to snap on degrees (more useful than snapping
|
|
2429
|
+
// on coordinates) by setting snapDegrees to a positive integer.
|
|
2430
|
+
//
|
|
2431
|
+
// The returned object includes the following properties/methods:
|
|
2432
|
+
//
|
|
2433
|
+
// - movableAngle.points
|
|
2434
|
+
// The movableAngle's dynamic smartPoints.
|
|
2435
|
+
//
|
|
2436
|
+
// - movableAngle.coords
|
|
2437
|
+
// The movableAngle's current coordinates (generated, don't edit).
|
|
2438
|
+
//
|
|
2439
|
+
addMovableAngle: function (options) {
|
|
2440
|
+
return new MovableAngle(this, options);
|
|
2441
|
+
},
|
|
2442
|
+
|
|
2443
|
+
// center: movable point
|
|
2444
|
+
// radius: int
|
|
2445
|
+
// circ: graphie circle
|
|
2446
|
+
// perim: invisible mouse target for dragging/changing radius
|
|
2447
|
+
addCircleGraph: function (options) {
|
|
2448
|
+
const graphie = this;
|
|
2449
|
+
const circle = $.extend(
|
|
2450
|
+
{
|
|
2451
|
+
center: [0, 0],
|
|
2452
|
+
radius: 2,
|
|
2453
|
+
snapX: 0.5,
|
|
2454
|
+
snapY: 0.5,
|
|
2455
|
+
snapRadius: 0.5,
|
|
2456
|
+
minRadius: 1,
|
|
2457
|
+
centerConstraints: {},
|
|
2458
|
+
centerNormalStyle: {},
|
|
2459
|
+
centerHighlightStyle: {
|
|
2460
|
+
stroke: KhanColors.INTERACTING,
|
|
2461
|
+
fill: KhanColors.INTERACTING,
|
|
2462
|
+
},
|
|
2463
|
+
circleNormalStyle: {
|
|
2464
|
+
stroke: KhanColors.INTERACTIVE,
|
|
2465
|
+
"fill-opacity": 0,
|
|
2466
|
+
},
|
|
2467
|
+
circleHighlightStyle: {
|
|
2468
|
+
stroke: KhanColors.INTERACTING,
|
|
2469
|
+
fill: KhanColors.INTERACTING,
|
|
2470
|
+
"fill-opacity": 0.05,
|
|
2471
|
+
},
|
|
2472
|
+
},
|
|
2473
|
+
options,
|
|
2474
|
+
);
|
|
2475
|
+
|
|
2476
|
+
const normalColor = circle.centerConstraints.fixed
|
|
2477
|
+
? KhanColors.DYNAMIC
|
|
2478
|
+
: KhanColors.INTERACTIVE;
|
|
2479
|
+
const centerNormalStyle = options ? options.centerNormalStyle : null;
|
|
2480
|
+
circle.centerNormalStyle = _.extend(
|
|
2481
|
+
{},
|
|
2482
|
+
{
|
|
2483
|
+
fill: normalColor,
|
|
2484
|
+
stroke: normalColor,
|
|
2485
|
+
},
|
|
2486
|
+
centerNormalStyle,
|
|
2487
|
+
);
|
|
2488
|
+
|
|
2489
|
+
circle.centerPoint = graphie.addMovablePoint({
|
|
2490
|
+
graph: graphie,
|
|
2491
|
+
coord: circle.center,
|
|
2492
|
+
normalStyle: circle.centerNormalStyle,
|
|
2493
|
+
snapX: circle.snapX,
|
|
2494
|
+
snapY: circle.snapY,
|
|
2495
|
+
constraints: circle.centerConstraints,
|
|
2496
|
+
});
|
|
2497
|
+
circle.circ = graphie.circle(
|
|
2498
|
+
circle.center,
|
|
2499
|
+
circle.radius,
|
|
2500
|
+
circle.circleNormalStyle,
|
|
2501
|
+
);
|
|
2502
|
+
circle.perim = graphie.mouselayer
|
|
2503
|
+
.circle(
|
|
2504
|
+
graphie.scalePoint(circle.center)[0],
|
|
2505
|
+
graphie.scalePoint(circle.center)[1],
|
|
2506
|
+
graphie.scaleVector(circle.radius)[0],
|
|
2507
|
+
)
|
|
2508
|
+
.attr({
|
|
2509
|
+
"stroke-width": 20,
|
|
2510
|
+
opacity: 0.002, // This is as close to 0 as MSIE will allow
|
|
2511
|
+
});
|
|
2512
|
+
|
|
2513
|
+
// Highlight circle circumference on center point hover
|
|
2514
|
+
if (!circle.centerConstraints.fixed) {
|
|
2515
|
+
$(circle.centerPoint.mouseTarget.getMouseTarget()).on(
|
|
2516
|
+
"vmouseover vmouseout",
|
|
2517
|
+
function (event) {
|
|
2518
|
+
if (
|
|
2519
|
+
circle.centerPoint.highlight ||
|
|
2520
|
+
circle.centerPoint.dragging
|
|
2521
|
+
) {
|
|
2522
|
+
circle.circ.animate(circle.circleHighlightStyle, 50);
|
|
2523
|
+
} else {
|
|
2524
|
+
circle.circ.animate(circle.circleNormalStyle, 50);
|
|
2525
|
+
}
|
|
2526
|
+
},
|
|
2527
|
+
);
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
circle.toFront = function () {
|
|
2531
|
+
circle.circ.toFront();
|
|
2532
|
+
circle.perim.toFront();
|
|
2533
|
+
circle.centerPoint.visibleShape.toFront();
|
|
2534
|
+
if (!circle.centerConstraints.fixed) {
|
|
2535
|
+
circle.centerPoint.mouseTarget.toFront();
|
|
2536
|
+
}
|
|
2537
|
+
};
|
|
2538
|
+
|
|
2539
|
+
circle.centerPoint.onMove = function (x: any, y: any) {
|
|
2540
|
+
circle.toFront();
|
|
2541
|
+
circle.circ.attr({
|
|
2542
|
+
cx: graphie.scalePoint(x)[0],
|
|
2543
|
+
cy: graphie.scalePoint(y)[1],
|
|
2544
|
+
});
|
|
2545
|
+
circle.perim.attr({
|
|
2546
|
+
cx: graphie.scalePoint(x)[0],
|
|
2547
|
+
cy: graphie.scalePoint(y)[1],
|
|
2548
|
+
});
|
|
2549
|
+
if (circle.onMove) {
|
|
2550
|
+
circle.onMove(x, y);
|
|
2551
|
+
}
|
|
2552
|
+
};
|
|
2553
|
+
|
|
2554
|
+
$(circle.centerPoint).on("move", function () {
|
|
2555
|
+
circle.center = this.coord;
|
|
2556
|
+
$(circle).trigger("move");
|
|
2557
|
+
});
|
|
2558
|
+
|
|
2559
|
+
// circle.setCenter(x, y) moves the circle to the specified
|
|
2560
|
+
// x, y coordinate as if the user had dragged it there.
|
|
2561
|
+
circle.setCenter = function (x: any, y: any) {
|
|
2562
|
+
circle.centerPoint.setCoord([x, y]);
|
|
2563
|
+
circle.centerPoint.onMove(x, y);
|
|
2564
|
+
circle.center = [x, y];
|
|
2565
|
+
};
|
|
2566
|
+
|
|
2567
|
+
// circle.setRadius(r) sets the circle's radius to the specified
|
|
2568
|
+
// value as if the user had dragged it there.
|
|
2569
|
+
circle.setRadius = function (r: any) {
|
|
2570
|
+
circle.radius = r;
|
|
2571
|
+
|
|
2572
|
+
circle.perim.attr({
|
|
2573
|
+
r: graphie.scaleVector(r)[0],
|
|
2574
|
+
});
|
|
2575
|
+
circle.circ.attr({
|
|
2576
|
+
rx: graphie.scaleVector(r)[0],
|
|
2577
|
+
ry: graphie.scaleVector(r)[1],
|
|
2578
|
+
});
|
|
2579
|
+
};
|
|
2580
|
+
|
|
2581
|
+
circle.remove = function () {
|
|
2582
|
+
circle.centerPoint.remove();
|
|
2583
|
+
circle.circ.remove();
|
|
2584
|
+
circle.perim.remove();
|
|
2585
|
+
};
|
|
2586
|
+
|
|
2587
|
+
// Define a set of axes using polar coordinates to specify
|
|
2588
|
+
// which resizing cursor we want to show based on where the
|
|
2589
|
+
// mouse position lies in relation to the circle's center.
|
|
2590
|
+
// The first two columns in cursorAxes refer to the minimum
|
|
2591
|
+
// and maximum angle values bounding a circle sector, and
|
|
2592
|
+
// the third column refers to the cursor name that will be
|
|
2593
|
+
// applied if the mouse position falls inside the given sector.
|
|
2594
|
+
const cursorAxes = [
|
|
2595
|
+
[Math.PI * -1.0, Math.PI * -0.875, "ew-resize"],
|
|
2596
|
+
[Math.PI * -0.875, Math.PI * -0.625, "nesw-resize"],
|
|
2597
|
+
[Math.PI * -0.625, Math.PI * -0.375, "ns-resize"],
|
|
2598
|
+
[Math.PI * -0.375, Math.PI * -0.125, "nwse-resize"],
|
|
2599
|
+
[Math.PI * -0.125, Math.PI * 0.125, "ew-resize"],
|
|
2600
|
+
[Math.PI * 0.125, Math.PI * 0.375, "nesw-resize"],
|
|
2601
|
+
[Math.PI * 0.375, Math.PI * 0.625, "ns-resize"],
|
|
2602
|
+
[Math.PI * 0.625, Math.PI * 0.875, "nwse-resize"],
|
|
2603
|
+
[Math.PI * 0.875, Math.PI * 1.0, "ew-resize"],
|
|
2604
|
+
];
|
|
2605
|
+
|
|
2606
|
+
// When the mouse moves along the circle's perimeter, we
|
|
2607
|
+
// dynamically set a CSS rule to show the correct
|
|
2608
|
+
// bidirectional cursor so a student knows they can resize
|
|
2609
|
+
// our circle. To do this, we convert the x and y coordinates
|
|
2610
|
+
// of the mouse position into polar coordinates and use the
|
|
2611
|
+
// defined cursorAxes above to set our rule.
|
|
2612
|
+
$(circle.perim.node).on("vmousemove", (event) => {
|
|
2613
|
+
let [x, y] = this.getMouseCoord(event);
|
|
2614
|
+
|
|
2615
|
+
x -= circle.center[0];
|
|
2616
|
+
y -= circle.center[1];
|
|
2617
|
+
|
|
2618
|
+
const theta = Math.atan2(y, x);
|
|
2619
|
+
|
|
2620
|
+
cursorAxes.forEach(function (axes) {
|
|
2621
|
+
const [min, max, cursorName] = axes;
|
|
2622
|
+
if (theta >= min && theta < max) {
|
|
2623
|
+
$(circle.perim.node).css("cursor", cursorName);
|
|
2624
|
+
}
|
|
2625
|
+
});
|
|
2626
|
+
});
|
|
2627
|
+
|
|
2628
|
+
// Set a default resizing-friendly cursor to be safe.
|
|
2629
|
+
$(circle.perim.node).css("cursor", "nesw-resize");
|
|
2630
|
+
|
|
2631
|
+
// Prevent the page from scrolling when we grab and drag the circle on
|
|
2632
|
+
// a mobile device.
|
|
2633
|
+
circle.perim.node.addEventListener(
|
|
2634
|
+
"touchstart",
|
|
2635
|
+
function (event) {
|
|
2636
|
+
event.preventDefault();
|
|
2637
|
+
},
|
|
2638
|
+
{passive: false},
|
|
2639
|
+
);
|
|
2640
|
+
|
|
2641
|
+
$(circle.perim.node).on(
|
|
2642
|
+
"vmouseover vmouseout vmousedown",
|
|
2643
|
+
function (event) {
|
|
2644
|
+
if (event.type === "vmouseover") {
|
|
2645
|
+
circle.highlight = true;
|
|
2646
|
+
if (!dragging) {
|
|
2647
|
+
// TODO(jack): Figure out why this doesn't work
|
|
2648
|
+
// for circleHighlightStyle's that change
|
|
2649
|
+
// stroke-dasharray
|
|
2650
|
+
circle.circ.animate(circle.circleHighlightStyle, 50);
|
|
2651
|
+
circle.centerPoint.visibleShape.animate(
|
|
2652
|
+
circle.centerHighlightStyle,
|
|
2653
|
+
50,
|
|
2654
|
+
);
|
|
2655
|
+
}
|
|
2656
|
+
} else if (event.type === "vmouseout") {
|
|
2657
|
+
circle.highlight = false;
|
|
2658
|
+
if (!circle.dragging && !circle.centerPoint.dragging) {
|
|
2659
|
+
circle.circ.animate(circle.circleNormalStyle, 50);
|
|
2660
|
+
circle.centerPoint.visibleShape.animate(
|
|
2661
|
+
circle.centerNormalStyle,
|
|
2662
|
+
50,
|
|
2663
|
+
);
|
|
2664
|
+
}
|
|
2665
|
+
} else if (
|
|
2666
|
+
event.type === "vmousedown" &&
|
|
2667
|
+
(event.which === 1 || event.which === 0)
|
|
2668
|
+
) {
|
|
2669
|
+
event.preventDefault();
|
|
2670
|
+
circle.toFront();
|
|
2671
|
+
const startRadius = circle.radius;
|
|
2672
|
+
|
|
2673
|
+
$(document).on("vmousemove vmouseup", function (event) {
|
|
2674
|
+
event.preventDefault();
|
|
2675
|
+
circle.dragging = true;
|
|
2676
|
+
dragging = true;
|
|
2677
|
+
|
|
2678
|
+
if (event.type === "vmousemove") {
|
|
2679
|
+
const coord = graphie.constrainToBounds(
|
|
2680
|
+
graphie.getMouseCoord(event),
|
|
2681
|
+
10,
|
|
2682
|
+
);
|
|
2683
|
+
|
|
2684
|
+
let radius = GraphUtils.getDistance(
|
|
2685
|
+
circle.centerPoint.coord,
|
|
2686
|
+
coord,
|
|
2687
|
+
);
|
|
2688
|
+
radius = Math.max(
|
|
2689
|
+
circle.minRadius,
|
|
2690
|
+
Math.round(radius / circle.snapRadius) *
|
|
2691
|
+
circle.snapRadius,
|
|
2692
|
+
);
|
|
2693
|
+
const oldRadius = circle.radius;
|
|
2694
|
+
let doResize = true;
|
|
2695
|
+
if (circle.onResize) {
|
|
2696
|
+
const onResizeResult = circle.onResize(
|
|
2697
|
+
radius,
|
|
2698
|
+
oldRadius,
|
|
2699
|
+
);
|
|
2700
|
+
if (_.isNumber(onResizeResult)) {
|
|
2701
|
+
radius = onResizeResult;
|
|
2702
|
+
} else if (onResizeResult === false) {
|
|
2703
|
+
doResize = false;
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
if (doResize) {
|
|
2707
|
+
circle.setRadius(radius);
|
|
2708
|
+
$(circle).trigger("move");
|
|
2709
|
+
}
|
|
2710
|
+
} else if (event.type === "vmouseup") {
|
|
2711
|
+
$(document).off("vmousemove vmouseup");
|
|
2712
|
+
circle.dragging = false;
|
|
2713
|
+
dragging = false;
|
|
2714
|
+
if (circle.onResizeEnd) {
|
|
2715
|
+
circle.onResizeEnd(circle.radius, startRadius);
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
});
|
|
2719
|
+
}
|
|
2720
|
+
},
|
|
2721
|
+
);
|
|
2722
|
+
|
|
2723
|
+
return circle;
|
|
2724
|
+
},
|
|
2725
|
+
|
|
2726
|
+
addRotateHandle: (function () {
|
|
2727
|
+
const drawRotateHandle = function (
|
|
2728
|
+
graphie,
|
|
2729
|
+
center: any,
|
|
2730
|
+
radius,
|
|
2731
|
+
halfWidth,
|
|
2732
|
+
lengthAngle,
|
|
2733
|
+
angle,
|
|
2734
|
+
interacting,
|
|
2735
|
+
) {
|
|
2736
|
+
const getRotateHandlePoint = function (
|
|
2737
|
+
offset: number,
|
|
2738
|
+
distanceFromArrowMidline,
|
|
2739
|
+
) {
|
|
2740
|
+
const distFromRotationCenter =
|
|
2741
|
+
radius + distanceFromArrowMidline;
|
|
2742
|
+
const vec = kvector.cartFromPolarDeg(
|
|
2743
|
+
distFromRotationCenter,
|
|
2744
|
+
angle + offset,
|
|
2745
|
+
);
|
|
2746
|
+
const absolute = kvector.add(center, vec);
|
|
2747
|
+
const pixels = graphie.scalePoint(absolute);
|
|
2748
|
+
return pixels[0] + "," + pixels[1];
|
|
2749
|
+
};
|
|
2750
|
+
|
|
2751
|
+
const innerR = graphie.scaleVector(radius - halfWidth);
|
|
2752
|
+
const outerR = graphie.scaleVector(radius + halfWidth);
|
|
2753
|
+
|
|
2754
|
+
// Draw the double-headed arrow thing that shows users where to
|
|
2755
|
+
// click and drag to rotate
|
|
2756
|
+
return graphie.raphael
|
|
2757
|
+
.path(
|
|
2758
|
+
// upper arrowhead
|
|
2759
|
+
" M" +
|
|
2760
|
+
getRotateHandlePoint(lengthAngle, -halfWidth) +
|
|
2761
|
+
" L" +
|
|
2762
|
+
getRotateHandlePoint(lengthAngle, -3 * halfWidth) +
|
|
2763
|
+
" L" +
|
|
2764
|
+
getRotateHandlePoint(2 * lengthAngle, 0) +
|
|
2765
|
+
" L" +
|
|
2766
|
+
getRotateHandlePoint(lengthAngle, 3 * halfWidth) +
|
|
2767
|
+
" L" +
|
|
2768
|
+
getRotateHandlePoint(lengthAngle, halfWidth) +
|
|
2769
|
+
// outer arc
|
|
2770
|
+
" A" +
|
|
2771
|
+
outerR[0] +
|
|
2772
|
+
"," +
|
|
2773
|
+
outerR[1] +
|
|
2774
|
+
",0,0,1," +
|
|
2775
|
+
getRotateHandlePoint(-lengthAngle, halfWidth) +
|
|
2776
|
+
// lower arrowhead
|
|
2777
|
+
" L" +
|
|
2778
|
+
getRotateHandlePoint(-lengthAngle, 3 * halfWidth) +
|
|
2779
|
+
" L" +
|
|
2780
|
+
getRotateHandlePoint(-2 * lengthAngle, 0) +
|
|
2781
|
+
" L" +
|
|
2782
|
+
getRotateHandlePoint(-lengthAngle, -3 * halfWidth) +
|
|
2783
|
+
" L" +
|
|
2784
|
+
getRotateHandlePoint(-lengthAngle, -halfWidth) +
|
|
2785
|
+
// inner arc
|
|
2786
|
+
" A" +
|
|
2787
|
+
innerR[0] +
|
|
2788
|
+
"," +
|
|
2789
|
+
innerR[1] +
|
|
2790
|
+
",0,0,0," +
|
|
2791
|
+
getRotateHandlePoint(lengthAngle, -halfWidth) +
|
|
2792
|
+
" Z",
|
|
2793
|
+
)
|
|
2794
|
+
.attr({
|
|
2795
|
+
stroke: null,
|
|
2796
|
+
fill: interacting
|
|
2797
|
+
? KhanColors.INTERACTING
|
|
2798
|
+
: KhanColors.INTERACTIVE,
|
|
2799
|
+
});
|
|
2800
|
+
};
|
|
2801
|
+
|
|
2802
|
+
return function (options: any) {
|
|
2803
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
2804
|
+
const graph = this;
|
|
2805
|
+
|
|
2806
|
+
let rotatePoint = options.center;
|
|
2807
|
+
const radius = options.radius;
|
|
2808
|
+
const lengthAngle = options.lengthAngle || 30;
|
|
2809
|
+
const hideArrow = options.hideArrow || false;
|
|
2810
|
+
const mouseTarget = options.mouseTarget;
|
|
2811
|
+
const id = _.uniqueId("rotateHandle");
|
|
2812
|
+
|
|
2813
|
+
// Normalize rotatePoint into something that always looks
|
|
2814
|
+
// like a movablePoint
|
|
2815
|
+
if (_.isArray(rotatePoint)) {
|
|
2816
|
+
rotatePoint = {
|
|
2817
|
+
coord: rotatePoint,
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
const rotateHandle = graph.addMovablePoint({
|
|
2822
|
+
coord: kpoint.addVector(
|
|
2823
|
+
rotatePoint.coord,
|
|
2824
|
+
kvector.cartFromPolarDeg(radius, options.angleDeg || 0),
|
|
2825
|
+
),
|
|
2826
|
+
constraints: {
|
|
2827
|
+
fixedDistance: {
|
|
2828
|
+
dist: radius,
|
|
2829
|
+
point: rotatePoint,
|
|
2830
|
+
},
|
|
2831
|
+
},
|
|
2832
|
+
mouseTarget: mouseTarget,
|
|
2833
|
+
});
|
|
2834
|
+
|
|
2835
|
+
// move the rotatePoint in front of the rotateHandle to avoid
|
|
2836
|
+
// confusing clicking/scaling of the rotateHandle when the user
|
|
2837
|
+
// intends to click on the rotatePoint
|
|
2838
|
+
rotatePoint.toFront();
|
|
2839
|
+
|
|
2840
|
+
let rotatePointPrevCoord = rotatePoint.coord;
|
|
2841
|
+
let rotateHandlePrevCoord = rotateHandle.coord;
|
|
2842
|
+
let rotateHandleStartCoord = rotateHandlePrevCoord;
|
|
2843
|
+
let isRotating = false;
|
|
2844
|
+
let isHovering = false;
|
|
2845
|
+
let drawnRotateHandle;
|
|
2846
|
+
|
|
2847
|
+
const redrawRotateHandle = function (
|
|
2848
|
+
handleCoord: Array<never> | ReadonlyArray<number>,
|
|
2849
|
+
) {
|
|
2850
|
+
if (hideArrow) {
|
|
2851
|
+
return; // Don't draw anything!
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
const handleVec = kvector.subtract(
|
|
2855
|
+
handleCoord,
|
|
2856
|
+
rotatePoint.coord,
|
|
2857
|
+
);
|
|
2858
|
+
const handlePolar = kvector.polarDegFromCart(handleVec);
|
|
2859
|
+
const angle = handlePolar[1];
|
|
2860
|
+
|
|
2861
|
+
if (drawnRotateHandle) {
|
|
2862
|
+
drawnRotateHandle.remove();
|
|
2863
|
+
}
|
|
2864
|
+
|
|
2865
|
+
drawnRotateHandle = drawRotateHandle(
|
|
2866
|
+
graph,
|
|
2867
|
+
rotatePoint.coord,
|
|
2868
|
+
options.radius,
|
|
2869
|
+
isRotating || isHovering
|
|
2870
|
+
? options.hoverWidth / 2
|
|
2871
|
+
: options.width / 2,
|
|
2872
|
+
lengthAngle,
|
|
2873
|
+
angle,
|
|
2874
|
+
isRotating || isHovering,
|
|
2875
|
+
);
|
|
2876
|
+
};
|
|
2877
|
+
|
|
2878
|
+
// when the rotation center moves, we need to move
|
|
2879
|
+
// the rotationHandle as well, or it will end up out
|
|
2880
|
+
// of sync
|
|
2881
|
+
$(rotatePoint).on("move." + id, function () {
|
|
2882
|
+
const delta = kvector.subtract(
|
|
2883
|
+
rotatePoint.coord,
|
|
2884
|
+
rotatePointPrevCoord,
|
|
2885
|
+
);
|
|
2886
|
+
|
|
2887
|
+
rotateHandle.setCoord(kvector.add(rotateHandle.coord, delta));
|
|
2888
|
+
|
|
2889
|
+
redrawRotateHandle(rotateHandle.coord);
|
|
2890
|
+
|
|
2891
|
+
rotatePointPrevCoord = rotatePoint.coord;
|
|
2892
|
+
rotateHandle.constraints.fixedDistance.point = rotatePoint;
|
|
2893
|
+
rotateHandlePrevCoord = rotateHandle.coord;
|
|
2894
|
+
});
|
|
2895
|
+
|
|
2896
|
+
// Rotate polygon with rotateHandle
|
|
2897
|
+
rotateHandle.onMove = function (x, y) {
|
|
2898
|
+
if (!isRotating) {
|
|
2899
|
+
rotateHandleStartCoord = rotateHandlePrevCoord;
|
|
2900
|
+
isRotating = true;
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2903
|
+
let coord = [x, y];
|
|
2904
|
+
|
|
2905
|
+
if (options.onMove) {
|
|
2906
|
+
const oldPolar = kvector.polarDegFromCart(
|
|
2907
|
+
kvector.subtract(
|
|
2908
|
+
rotateHandlePrevCoord,
|
|
2909
|
+
rotatePoint.coord,
|
|
2910
|
+
),
|
|
2911
|
+
);
|
|
2912
|
+
const newPolar = kvector.polarDegFromCart(
|
|
2913
|
+
kvector.subtract(coord, rotatePoint.coord),
|
|
2914
|
+
);
|
|
2915
|
+
|
|
2916
|
+
const oldAngle = oldPolar[1];
|
|
2917
|
+
const newAngle = newPolar[1];
|
|
2918
|
+
let result = options.onMove(newAngle, oldAngle);
|
|
2919
|
+
if (result != null && result !== true) {
|
|
2920
|
+
if (result === false) {
|
|
2921
|
+
result = oldAngle;
|
|
2922
|
+
}
|
|
2923
|
+
coord = kvector.add(
|
|
2924
|
+
rotatePoint.coord,
|
|
2925
|
+
kvector.cartFromPolarDeg(oldPolar[0], result),
|
|
2926
|
+
);
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
redrawRotateHandle(coord);
|
|
2931
|
+
|
|
2932
|
+
rotateHandlePrevCoord = coord;
|
|
2933
|
+
return coord;
|
|
2934
|
+
};
|
|
2935
|
+
|
|
2936
|
+
rotateHandle.onMoveEnd = function () {
|
|
2937
|
+
isRotating = false;
|
|
2938
|
+
redrawRotateHandle(rotateHandle.coord);
|
|
2939
|
+
if (options.onMoveEnd) {
|
|
2940
|
+
const oldPolar = kvector.polarDegFromCart(
|
|
2941
|
+
kvector.subtract(
|
|
2942
|
+
rotateHandleStartCoord,
|
|
2943
|
+
rotatePoint.coord,
|
|
2944
|
+
),
|
|
2945
|
+
);
|
|
2946
|
+
const newPolar = kvector.polarDegFromCart(
|
|
2947
|
+
kvector.subtract(rotateHandle.coord, rotatePoint.coord),
|
|
2948
|
+
);
|
|
2949
|
+
options.onMoveEnd(newPolar[1], oldPolar[1]);
|
|
2950
|
+
}
|
|
2951
|
+
};
|
|
2952
|
+
|
|
2953
|
+
// Remove the default dot added by the movablePoint since we have
|
|
2954
|
+
// our double-arrow thing
|
|
2955
|
+
rotateHandle.visibleShape.remove();
|
|
2956
|
+
|
|
2957
|
+
if (!mouseTarget) {
|
|
2958
|
+
// Make the default mouse target bigger to encompass the whole
|
|
2959
|
+
// area around the double-arrow thing
|
|
2960
|
+
rotateHandle.mouseTarget.attr({scale: 2});
|
|
2961
|
+
}
|
|
2962
|
+
|
|
2963
|
+
const $mouseTarget = $(rotateHandle.mouseTarget.getMouseTarget());
|
|
2964
|
+
$mouseTarget.bind("vmouseover", function (e) {
|
|
2965
|
+
isHovering = true;
|
|
2966
|
+
redrawRotateHandle(rotateHandle.coord);
|
|
2967
|
+
});
|
|
2968
|
+
$mouseTarget.bind("vmouseout", function (e) {
|
|
2969
|
+
isHovering = false;
|
|
2970
|
+
redrawRotateHandle(rotateHandle.coord);
|
|
2971
|
+
});
|
|
2972
|
+
|
|
2973
|
+
redrawRotateHandle(rotateHandle.coord);
|
|
2974
|
+
|
|
2975
|
+
const oldRemove = rotateHandle.remove;
|
|
2976
|
+
rotateHandle.remove = function () {
|
|
2977
|
+
oldRemove.call(rotateHandle);
|
|
2978
|
+
if (drawnRotateHandle) {
|
|
2979
|
+
drawnRotateHandle.remove();
|
|
2980
|
+
}
|
|
2981
|
+
$(rotatePoint).off("move." + id);
|
|
2982
|
+
};
|
|
2983
|
+
|
|
2984
|
+
rotateHandle.update = function () {
|
|
2985
|
+
redrawRotateHandle(rotateHandle.coord);
|
|
2986
|
+
};
|
|
2987
|
+
|
|
2988
|
+
return rotateHandle;
|
|
2989
|
+
};
|
|
2990
|
+
})(),
|
|
2991
|
+
|
|
2992
|
+
addReflectButton: (function () {
|
|
2993
|
+
const drawButton = function (
|
|
2994
|
+
graphie,
|
|
2995
|
+
buttonCoord: kpoint.Point,
|
|
2996
|
+
lineCoords: Array<any> | Array<never> | Array<any | Array<number>>,
|
|
2997
|
+
size: number,
|
|
2998
|
+
distanceFromCenter: number,
|
|
2999
|
+
leftStyle: any,
|
|
3000
|
+
rightStyle: any,
|
|
3001
|
+
) {
|
|
3002
|
+
// Avoid invalid lines
|
|
3003
|
+
if (kpoint.equal(lineCoords[0], lineCoords[1])) {
|
|
3004
|
+
lineCoords = [
|
|
3005
|
+
lineCoords[0],
|
|
3006
|
+
kpoint.addVector(lineCoords[0], [1, 1]),
|
|
3007
|
+
];
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
const lineDirection = kvector.normalize(
|
|
3011
|
+
kvector.subtract(lineCoords[1], lineCoords[0]),
|
|
3012
|
+
);
|
|
3013
|
+
|
|
3014
|
+
const lineVec = kvector.scale(lineDirection, size / 2);
|
|
3015
|
+
|
|
3016
|
+
const centerVec = kvector.scale(lineDirection, distanceFromCenter);
|
|
3017
|
+
const leftCenterVec = kvector.rotateDeg(centerVec, 90);
|
|
3018
|
+
const rightCenterVec = kvector.rotateDeg(centerVec, -90);
|
|
3019
|
+
|
|
3020
|
+
const negLineVec = kvector.negate(lineVec);
|
|
3021
|
+
const leftVec = kvector.rotateDeg(lineVec, 90);
|
|
3022
|
+
const rightVec = kvector.rotateDeg(lineVec, -90);
|
|
3023
|
+
|
|
3024
|
+
const leftCenter = kpoint.addVectors(buttonCoord, leftCenterVec);
|
|
3025
|
+
const rightCenter = kpoint.addVectors(buttonCoord, rightCenterVec);
|
|
3026
|
+
|
|
3027
|
+
const leftCoord1 = kpoint.addVectors(
|
|
3028
|
+
buttonCoord,
|
|
3029
|
+
leftCenterVec,
|
|
3030
|
+
lineVec,
|
|
3031
|
+
leftVec,
|
|
3032
|
+
);
|
|
3033
|
+
const leftCoord2 = kpoint.addVectors(
|
|
3034
|
+
buttonCoord,
|
|
3035
|
+
leftCenterVec,
|
|
3036
|
+
negLineVec,
|
|
3037
|
+
leftVec,
|
|
3038
|
+
);
|
|
3039
|
+
const rightCoord1 = kpoint.addVectors(
|
|
3040
|
+
buttonCoord,
|
|
3041
|
+
rightCenterVec,
|
|
3042
|
+
lineVec,
|
|
3043
|
+
rightVec,
|
|
3044
|
+
);
|
|
3045
|
+
const rightCoord2 = kpoint.addVectors(
|
|
3046
|
+
buttonCoord,
|
|
3047
|
+
rightCenterVec,
|
|
3048
|
+
negLineVec,
|
|
3049
|
+
rightVec,
|
|
3050
|
+
);
|
|
3051
|
+
|
|
3052
|
+
const leftButton = graphie.path(
|
|
3053
|
+
[leftCenter, leftCoord1, leftCoord2, true],
|
|
3054
|
+
leftStyle,
|
|
3055
|
+
);
|
|
3056
|
+
const rightButton = graphie.path(
|
|
3057
|
+
[rightCenter, rightCoord1, rightCoord2, true],
|
|
3058
|
+
rightStyle,
|
|
3059
|
+
);
|
|
3060
|
+
|
|
3061
|
+
return {
|
|
3062
|
+
remove: function () {
|
|
3063
|
+
leftButton.remove();
|
|
3064
|
+
rightButton.remove();
|
|
3065
|
+
},
|
|
3066
|
+
};
|
|
3067
|
+
};
|
|
3068
|
+
|
|
3069
|
+
return function (options: any) {
|
|
3070
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3071
|
+
const graphie = this;
|
|
3072
|
+
|
|
3073
|
+
const line = options.line;
|
|
3074
|
+
|
|
3075
|
+
const button = graphie.addMovablePoint({
|
|
3076
|
+
constraints: options.constraints,
|
|
3077
|
+
coord: kline.midpoint([line.pointA.coord, line.pointZ.coord]),
|
|
3078
|
+
snapX: graphie.snap[0],
|
|
3079
|
+
snapY: graphie.snap[1],
|
|
3080
|
+
onMove: function (x, y) {
|
|
3081
|
+
// Don't allow the button to actually move. This is a hack
|
|
3082
|
+
// around the inability to both set a point as fixed AND
|
|
3083
|
+
// allow it to be clicked.
|
|
3084
|
+
return false;
|
|
3085
|
+
},
|
|
3086
|
+
onMoveEnd: function (x, y) {
|
|
3087
|
+
if (options.onMoveEnd) {
|
|
3088
|
+
options.onMoveEnd.call(this, x, y);
|
|
3089
|
+
}
|
|
3090
|
+
},
|
|
3091
|
+
});
|
|
3092
|
+
|
|
3093
|
+
let isHovering = false;
|
|
3094
|
+
let isFlipped = false;
|
|
3095
|
+
let currentlyDrawnButton;
|
|
3096
|
+
|
|
3097
|
+
const isHighlight = function () {
|
|
3098
|
+
return isHovering;
|
|
3099
|
+
};
|
|
3100
|
+
|
|
3101
|
+
const styles = _.map([0, 1], function (isHighlight) {
|
|
3102
|
+
const baseStyle = isHighlight
|
|
3103
|
+
? options.highlightStyle
|
|
3104
|
+
: options.normalStyle;
|
|
3105
|
+
|
|
3106
|
+
return _.map([0, 1], function (opacity) {
|
|
3107
|
+
return _.defaults(
|
|
3108
|
+
{
|
|
3109
|
+
"fill-opacity": opacity,
|
|
3110
|
+
},
|
|
3111
|
+
baseStyle,
|
|
3112
|
+
);
|
|
3113
|
+
});
|
|
3114
|
+
});
|
|
3115
|
+
|
|
3116
|
+
const getStyle = function (isRight: number | boolean) {
|
|
3117
|
+
if (isFlipped) {
|
|
3118
|
+
isRight = !isRight;
|
|
3119
|
+
}
|
|
3120
|
+
return styles[+isHighlight()][+isRight];
|
|
3121
|
+
};
|
|
3122
|
+
|
|
3123
|
+
const redraw = function (
|
|
3124
|
+
coord: kpoint.Point,
|
|
3125
|
+
lineCoords: Array<any> | Array<never>,
|
|
3126
|
+
) {
|
|
3127
|
+
if (currentlyDrawnButton) {
|
|
3128
|
+
currentlyDrawnButton.remove();
|
|
3129
|
+
}
|
|
3130
|
+
currentlyDrawnButton = drawButton(
|
|
3131
|
+
graphie,
|
|
3132
|
+
coord,
|
|
3133
|
+
lineCoords,
|
|
3134
|
+
isHighlight() ? options.size * 1.5 : options.size,
|
|
3135
|
+
isHighlight() ? options.size * 0.125 : 0.25,
|
|
3136
|
+
getStyle(0),
|
|
3137
|
+
getStyle(1),
|
|
3138
|
+
);
|
|
3139
|
+
};
|
|
3140
|
+
|
|
3141
|
+
const update = function (coordA, coordZ) {
|
|
3142
|
+
coordA = coordA || line.pointA.coord;
|
|
3143
|
+
coordZ = coordZ || line.pointZ.coord;
|
|
3144
|
+
|
|
3145
|
+
const buttonCoord = kline.midpoint([coordA, coordZ]);
|
|
3146
|
+
button.setCoord(buttonCoord);
|
|
3147
|
+
|
|
3148
|
+
redraw(buttonCoord, [coordA, coordZ]);
|
|
3149
|
+
};
|
|
3150
|
+
|
|
3151
|
+
$(line).on("move", _.bind(update, button, null, null));
|
|
3152
|
+
|
|
3153
|
+
const $mouseTarget = $(button.mouseTarget.getMouseTarget());
|
|
3154
|
+
$mouseTarget.on("vclick", function () {
|
|
3155
|
+
const result = options.onClick();
|
|
3156
|
+
if (result !== false) {
|
|
3157
|
+
isFlipped = !isFlipped;
|
|
3158
|
+
redraw(button.coord, [
|
|
3159
|
+
line.pointA.coord,
|
|
3160
|
+
line.pointZ.coord,
|
|
3161
|
+
]);
|
|
3162
|
+
}
|
|
3163
|
+
});
|
|
3164
|
+
|
|
3165
|
+
// Bring the reflection line handles in front of the button, so
|
|
3166
|
+
// that if we drag the reflectPoints really close together, we can
|
|
3167
|
+
// still move the handles away from each other, rather than only
|
|
3168
|
+
// being able to apply the reflection.
|
|
3169
|
+
line.pointA.toFront();
|
|
3170
|
+
line.pointZ.toFront();
|
|
3171
|
+
|
|
3172
|
+
// Replace the visual point with the double triangle thing
|
|
3173
|
+
button.visibleShape.remove();
|
|
3174
|
+
const pointScale = graphie.scaleVector(options.size)[0] / 20;
|
|
3175
|
+
button.mouseTarget.attr({scale: 1.5 * pointScale});
|
|
3176
|
+
$mouseTarget.css("cursor", "pointer");
|
|
3177
|
+
|
|
3178
|
+
// Make the arrow-thing grow and shrink with mouseover/out
|
|
3179
|
+
$mouseTarget.bind("vmouseover", function (e) {
|
|
3180
|
+
isHovering = true;
|
|
3181
|
+
redraw(button.coord, [line.pointA.coord, line.pointZ.coord]);
|
|
3182
|
+
});
|
|
3183
|
+
$mouseTarget.bind("vmouseout", function (e) {
|
|
3184
|
+
isHovering = false;
|
|
3185
|
+
redraw(button.coord, [line.pointA.coord, line.pointZ.coord]);
|
|
3186
|
+
});
|
|
3187
|
+
|
|
3188
|
+
const oldButtonRemove = button.remove;
|
|
3189
|
+
button.remove = function () {
|
|
3190
|
+
currentlyDrawnButton.remove();
|
|
3191
|
+
oldButtonRemove.call(button);
|
|
3192
|
+
};
|
|
3193
|
+
|
|
3194
|
+
button.update = update;
|
|
3195
|
+
button.isFlipped = function () {
|
|
3196
|
+
return isFlipped;
|
|
3197
|
+
};
|
|
3198
|
+
|
|
3199
|
+
// @ts-expect-error [FEI-5003] - TS2554 - Expected 2 arguments, but got 0.
|
|
3200
|
+
update();
|
|
3201
|
+
return button;
|
|
3202
|
+
};
|
|
3203
|
+
})(),
|
|
3204
|
+
|
|
3205
|
+
protractor: function (center) {
|
|
3206
|
+
// @ts-expect-error [FEI-5003] - TS2350 - Only a void function can be called with the 'new' keyword.
|
|
3207
|
+
return new Protractor(this, center);
|
|
3208
|
+
},
|
|
3209
|
+
|
|
3210
|
+
ruler: function (options) {
|
|
3211
|
+
// @ts-expect-error [FEI-5003] - TS2350 - Only a void function can be called with the 'new' keyword.
|
|
3212
|
+
return new Ruler(this, options || {});
|
|
3213
|
+
},
|
|
3214
|
+
|
|
3215
|
+
addPoints: addPoints,
|
|
3216
|
+
});
|
|
3217
|
+
|
|
3218
|
+
function Protractor(graph: any, center: any) {
|
|
3219
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3220
|
+
this.set = graph.raphael.set();
|
|
3221
|
+
|
|
3222
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3223
|
+
this.cx = center[0];
|
|
3224
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3225
|
+
this.cy = center[1];
|
|
3226
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3227
|
+
const pro = this;
|
|
3228
|
+
|
|
3229
|
+
const r = graph.unscaleVector(180.5)[0];
|
|
3230
|
+
const imgPos = graph.scalePoint([
|
|
3231
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3232
|
+
this.cx - r,
|
|
3233
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3234
|
+
this.cy + r - graph.unscaleVector(10.5)[1],
|
|
3235
|
+
]);
|
|
3236
|
+
const image = graph.mouselayer.image(
|
|
3237
|
+
"https://ka-perseus-graphie.s3.amazonaws.com/e9d032f2ab8b95979f674fbfa67056442ba1ff6a.png",
|
|
3238
|
+
imgPos[0],
|
|
3239
|
+
imgPos[1],
|
|
3240
|
+
360,
|
|
3241
|
+
180,
|
|
3242
|
+
);
|
|
3243
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3244
|
+
this.set.push(image);
|
|
3245
|
+
|
|
3246
|
+
// Prevent the page from scrolling when we grab and drag the image on a
|
|
3247
|
+
// mobile device.
|
|
3248
|
+
image.node.addEventListener(
|
|
3249
|
+
"touchstart",
|
|
3250
|
+
function (event) {
|
|
3251
|
+
event.preventDefault();
|
|
3252
|
+
},
|
|
3253
|
+
{passive: false},
|
|
3254
|
+
);
|
|
3255
|
+
|
|
3256
|
+
const arrowHelper = function (angle: number, pixelsFromEdge: number) {
|
|
3257
|
+
const scaledRadius = graph.scaleVector(r);
|
|
3258
|
+
scaledRadius[0] -= 16;
|
|
3259
|
+
scaledRadius[1] -= 16;
|
|
3260
|
+
const scaledCenter = graph.scalePoint(center);
|
|
3261
|
+
const x =
|
|
3262
|
+
Math.sin(((angle + 90) * Math.PI) / 180) *
|
|
3263
|
+
(scaledRadius[0] + pixelsFromEdge) +
|
|
3264
|
+
scaledCenter[0];
|
|
3265
|
+
const y =
|
|
3266
|
+
Math.cos(((angle + 90) * Math.PI) / 180) *
|
|
3267
|
+
(scaledRadius[1] + pixelsFromEdge) +
|
|
3268
|
+
scaledCenter[1];
|
|
3269
|
+
return x + "," + y;
|
|
3270
|
+
};
|
|
3271
|
+
|
|
3272
|
+
const arrow = graph.raphael
|
|
3273
|
+
.path(
|
|
3274
|
+
" M" +
|
|
3275
|
+
arrowHelper(180, 6) +
|
|
3276
|
+
" L" +
|
|
3277
|
+
arrowHelper(180, 2) +
|
|
3278
|
+
" L" +
|
|
3279
|
+
arrowHelper(183, 10) +
|
|
3280
|
+
" L" +
|
|
3281
|
+
arrowHelper(180, 18) +
|
|
3282
|
+
" L" +
|
|
3283
|
+
arrowHelper(180, 14) +
|
|
3284
|
+
" A" +
|
|
3285
|
+
(graph.scaleVector(r)[0] + 10) +
|
|
3286
|
+
"," +
|
|
3287
|
+
(graph.scaleVector(r)[1] + 10) +
|
|
3288
|
+
",0,0,1," +
|
|
3289
|
+
arrowHelper(170, 14) +
|
|
3290
|
+
" L" +
|
|
3291
|
+
arrowHelper(170, 18) +
|
|
3292
|
+
" L" +
|
|
3293
|
+
arrowHelper(167, 10) +
|
|
3294
|
+
" L" +
|
|
3295
|
+
arrowHelper(170, 2) +
|
|
3296
|
+
" L" +
|
|
3297
|
+
arrowHelper(170, 6) +
|
|
3298
|
+
" A" +
|
|
3299
|
+
(graph.scaleVector(r)[0] + 10) +
|
|
3300
|
+
"," +
|
|
3301
|
+
(graph.scaleVector(r)[1] + 10) +
|
|
3302
|
+
",0,0,0," +
|
|
3303
|
+
arrowHelper(180, 6) +
|
|
3304
|
+
" Z",
|
|
3305
|
+
)
|
|
3306
|
+
.attr({
|
|
3307
|
+
stroke: null,
|
|
3308
|
+
fill: KhanColors.INTERACTIVE,
|
|
3309
|
+
});
|
|
3310
|
+
|
|
3311
|
+
// add it to the set so it translates with everything else
|
|
3312
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3313
|
+
this.set.push(arrow);
|
|
3314
|
+
|
|
3315
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3316
|
+
this.centerPoint = graph.addMovablePoint({
|
|
3317
|
+
coord: center,
|
|
3318
|
+
visible: false,
|
|
3319
|
+
});
|
|
3320
|
+
|
|
3321
|
+
// Use a movablePoint for rotation
|
|
3322
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3323
|
+
this.rotateHandle = graph.addMovablePoint({
|
|
3324
|
+
bounded: false,
|
|
3325
|
+
coord: [
|
|
3326
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3327
|
+
Math.sin((275 * Math.PI) / 180) * r + this.cx,
|
|
3328
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3329
|
+
Math.cos((275 * Math.PI) / 180) * r + this.cy,
|
|
3330
|
+
],
|
|
3331
|
+
onMove: function (x, y) {
|
|
3332
|
+
const angle =
|
|
3333
|
+
(Math.atan2(
|
|
3334
|
+
pro.centerPoint.coord[1] - y,
|
|
3335
|
+
pro.centerPoint.coord[0] - x,
|
|
3336
|
+
) *
|
|
3337
|
+
180) /
|
|
3338
|
+
Math.PI;
|
|
3339
|
+
pro.rotate(-angle - 5, true);
|
|
3340
|
+
},
|
|
3341
|
+
});
|
|
3342
|
+
|
|
3343
|
+
// Add a constraint so the point moves in a circle
|
|
3344
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3345
|
+
this.rotateHandle.constraints.fixedDistance.dist = r;
|
|
3346
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3347
|
+
this.rotateHandle.constraints.fixedDistance.point = this.centerPoint;
|
|
3348
|
+
|
|
3349
|
+
// Remove the default dot added by the movablePoint since we have our
|
|
3350
|
+
// double-arrow thing
|
|
3351
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3352
|
+
this.rotateHandle.visibleShape.remove();
|
|
3353
|
+
// Make the mouse target bigger to encompass the whole area around the
|
|
3354
|
+
// double-arrow thing
|
|
3355
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3356
|
+
this.rotateHandle.mouseTarget.attr({scale: 2.0});
|
|
3357
|
+
|
|
3358
|
+
let isDragging = false;
|
|
3359
|
+
let isHovering = false;
|
|
3360
|
+
const isHighlight = function () {
|
|
3361
|
+
return isHovering || isDragging;
|
|
3362
|
+
};
|
|
3363
|
+
|
|
3364
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3365
|
+
const self = this;
|
|
3366
|
+
const $mouseTarget = $(self.rotateHandle.mouseTarget.getMouseTarget());
|
|
3367
|
+
$mouseTarget.bind("vmousedown", function (event) {
|
|
3368
|
+
isDragging = true;
|
|
3369
|
+
$mouseTarget.css("cursor", "-webkit-grabbing");
|
|
3370
|
+
$mouseTarget.css("cursor", "grabbing");
|
|
3371
|
+
arrow.animate({scale: 1.5, fill: KhanColors.INTERACTING}, 50);
|
|
3372
|
+
|
|
3373
|
+
$(document).bind("vmouseup.rotateHandle", function (event) {
|
|
3374
|
+
isDragging = false;
|
|
3375
|
+
$mouseTarget.css("cursor", "-webkit-grab");
|
|
3376
|
+
$mouseTarget.css("cursor", "grab");
|
|
3377
|
+
|
|
3378
|
+
if (!isHighlight()) {
|
|
3379
|
+
arrow.animate({scale: 1.0, fill: KhanColors.INTERACTIVE}, 50);
|
|
3380
|
+
}
|
|
3381
|
+
|
|
3382
|
+
$(document).unbind("vmouseup.rotateHandle");
|
|
3383
|
+
});
|
|
3384
|
+
});
|
|
3385
|
+
|
|
3386
|
+
$mouseTarget.bind("vmouseover", function (event) {
|
|
3387
|
+
isHovering = true;
|
|
3388
|
+
arrow.animate({scale: 1.5, fill: KhanColors.INTERACTING}, 50);
|
|
3389
|
+
});
|
|
3390
|
+
$mouseTarget.bind("vmouseout", function (event) {
|
|
3391
|
+
isHovering = false;
|
|
3392
|
+
if (!isHighlight()) {
|
|
3393
|
+
arrow.animate({scale: 1.0, fill: KhanColors.INTERACTIVE}, 50);
|
|
3394
|
+
}
|
|
3395
|
+
});
|
|
3396
|
+
|
|
3397
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3398
|
+
const setNodes = $.map(this.set, function (el) {
|
|
3399
|
+
return el.node;
|
|
3400
|
+
});
|
|
3401
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3402
|
+
this.makeTranslatable = function makeTranslatable() {
|
|
3403
|
+
$(setNodes).css("cursor", "move");
|
|
3404
|
+
$mouseTarget.css("cursor", "-webkit-grab");
|
|
3405
|
+
$mouseTarget.css("cursor", "grab");
|
|
3406
|
+
|
|
3407
|
+
$(setNodes).bind("vmousedown", function (event) {
|
|
3408
|
+
event.preventDefault();
|
|
3409
|
+
let startx =
|
|
3410
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
3411
|
+
event.pageX - $(graph.raphael.canvas.parentNode).offset().left;
|
|
3412
|
+
let starty =
|
|
3413
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
3414
|
+
event.pageY - $(graph.raphael.canvas.parentNode).offset().top;
|
|
3415
|
+
|
|
3416
|
+
$(document).bind("vmousemove.protractor", function (event) {
|
|
3417
|
+
let mouseX =
|
|
3418
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
3419
|
+
event.pageX -
|
|
3420
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
3421
|
+
$(graph.raphael.canvas.parentNode).offset().left;
|
|
3422
|
+
let mouseY =
|
|
3423
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
3424
|
+
event.pageY -
|
|
3425
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
3426
|
+
$(graph.raphael.canvas.parentNode).offset().top;
|
|
3427
|
+
// can't go beyond 10 pixels from the edge
|
|
3428
|
+
mouseX = Math.max(10, Math.min(graph.xpixels - 10, mouseX));
|
|
3429
|
+
mouseY = Math.max(10, Math.min(graph.ypixels - 10, mouseY));
|
|
3430
|
+
|
|
3431
|
+
const dx = mouseX - startx;
|
|
3432
|
+
const dy = mouseY - starty;
|
|
3433
|
+
|
|
3434
|
+
$.each(pro.set.items, function () {
|
|
3435
|
+
this.translate(dx, dy);
|
|
3436
|
+
});
|
|
3437
|
+
pro.centerPoint.setCoord([
|
|
3438
|
+
pro.centerPoint.coord[0] + dx / graph.scale[0],
|
|
3439
|
+
pro.centerPoint.coord[1] - dy / graph.scale[1],
|
|
3440
|
+
]);
|
|
3441
|
+
pro.rotateHandle.setCoord([
|
|
3442
|
+
pro.rotateHandle.coord[0] + dx / graph.scale[0],
|
|
3443
|
+
pro.rotateHandle.coord[1] - dy / graph.scale[1],
|
|
3444
|
+
]);
|
|
3445
|
+
startx = mouseX;
|
|
3446
|
+
starty = mouseY;
|
|
3447
|
+
});
|
|
3448
|
+
|
|
3449
|
+
$(document).one("vmouseup", function (event) {
|
|
3450
|
+
$(document).unbind("vmousemove.protractor");
|
|
3451
|
+
});
|
|
3452
|
+
});
|
|
3453
|
+
};
|
|
3454
|
+
|
|
3455
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3456
|
+
this.rotation = 0;
|
|
3457
|
+
|
|
3458
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3459
|
+
this.rotate = function (offset: any, absolute: any) {
|
|
3460
|
+
const center = graph.scalePoint(this.centerPoint.coord);
|
|
3461
|
+
|
|
3462
|
+
if (absolute) {
|
|
3463
|
+
this.rotation = 0;
|
|
3464
|
+
}
|
|
3465
|
+
|
|
3466
|
+
this.set.rotate(this.rotation + offset, center[0], center[1]);
|
|
3467
|
+
this.rotation = this.rotation + offset;
|
|
3468
|
+
|
|
3469
|
+
return this;
|
|
3470
|
+
};
|
|
3471
|
+
|
|
3472
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3473
|
+
this.moveTo = function moveTo(x: any, y: any) {
|
|
3474
|
+
const start = graph.scalePoint(pro.centerPoint.coord);
|
|
3475
|
+
const end = graph.scalePoint([x, y]);
|
|
3476
|
+
const time = GraphUtils.getDistance(start, end) * 2;
|
|
3477
|
+
|
|
3478
|
+
$({x: start[0], y: start[1]}).animate(
|
|
3479
|
+
{x: end[0], y: end[1]},
|
|
3480
|
+
{
|
|
3481
|
+
duration: time,
|
|
3482
|
+
step: function (now, fx) {
|
|
3483
|
+
let dx = 0;
|
|
3484
|
+
let dy = 0;
|
|
3485
|
+
if (fx.prop === "x") {
|
|
3486
|
+
dx = now - graph.scalePoint(pro.centerPoint.coord)[0];
|
|
3487
|
+
} else if (fx.prop === "y") {
|
|
3488
|
+
dy = now - graph.scalePoint(pro.centerPoint.coord)[1];
|
|
3489
|
+
}
|
|
3490
|
+
$.each(pro.set.items, function () {
|
|
3491
|
+
this.translate(dx, dy);
|
|
3492
|
+
});
|
|
3493
|
+
pro.centerPoint.setCoord([
|
|
3494
|
+
pro.centerPoint.coord[0] + dx / graph.scale[0],
|
|
3495
|
+
pro.centerPoint.coord[1] - dy / graph.scale[1],
|
|
3496
|
+
]);
|
|
3497
|
+
pro.rotateHandle.setCoord([
|
|
3498
|
+
pro.rotateHandle.coord[0] + dx / graph.scale[0],
|
|
3499
|
+
pro.rotateHandle.coord[1] - dy / graph.scale[1],
|
|
3500
|
+
]);
|
|
3501
|
+
},
|
|
3502
|
+
},
|
|
3503
|
+
);
|
|
3504
|
+
};
|
|
3505
|
+
|
|
3506
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3507
|
+
this.rotateTo = function rotateTo(angle: any) {
|
|
3508
|
+
if (Math.abs(this.rotation - angle) > 180) {
|
|
3509
|
+
this.rotation += 360;
|
|
3510
|
+
}
|
|
3511
|
+
const time = Math.abs(this.rotation - angle) * 5;
|
|
3512
|
+
$({0: this.rotation}).animate(
|
|
3513
|
+
// $FlowFixMe[unsupported-syntax]
|
|
3514
|
+
{0: angle},
|
|
3515
|
+
{
|
|
3516
|
+
duration: time,
|
|
3517
|
+
step: function (now, fx) {
|
|
3518
|
+
pro.rotate(now, true);
|
|
3519
|
+
pro.rotateHandle.setCoord([
|
|
3520
|
+
Math.sin(((now + 275) * Math.PI) / 180) * r +
|
|
3521
|
+
pro.centerPoint.coord[0],
|
|
3522
|
+
Math.cos(((now + 275) * Math.PI) / 180) * r +
|
|
3523
|
+
pro.centerPoint.coord[1],
|
|
3524
|
+
]);
|
|
3525
|
+
},
|
|
3526
|
+
},
|
|
3527
|
+
);
|
|
3528
|
+
};
|
|
3529
|
+
|
|
3530
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3531
|
+
this.remove = function () {
|
|
3532
|
+
this.set.remove();
|
|
3533
|
+
};
|
|
3534
|
+
|
|
3535
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3536
|
+
this.makeTranslatable();
|
|
3537
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3538
|
+
return this;
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3541
|
+
function Ruler(graphie: any, options: any) {
|
|
3542
|
+
_.defaults(options, {
|
|
3543
|
+
center: [0, 0],
|
|
3544
|
+
pixelsPerUnit: 40,
|
|
3545
|
+
ticksPerUnit: 10, // 10 or power of 2
|
|
3546
|
+
units: 10, // the length the ruler can measure
|
|
3547
|
+
label: "", // e.g "cm" (the shorter, the better)
|
|
3548
|
+
style: {
|
|
3549
|
+
fill: null,
|
|
3550
|
+
stroke: KhanColors.GRAY,
|
|
3551
|
+
},
|
|
3552
|
+
});
|
|
3553
|
+
|
|
3554
|
+
const light = _.extend({}, options.style, {strokeWidth: 1});
|
|
3555
|
+
const bold = _.extend({}, options.style, {strokeWidth: 2});
|
|
3556
|
+
|
|
3557
|
+
const width = options.units * options.pixelsPerUnit;
|
|
3558
|
+
const height = 50;
|
|
3559
|
+
|
|
3560
|
+
const leftBottom = graphie.unscalePoint(
|
|
3561
|
+
kvector.subtract(
|
|
3562
|
+
graphie.scalePoint(options.center),
|
|
3563
|
+
kvector.scale([width, -height], 0.5),
|
|
3564
|
+
),
|
|
3565
|
+
);
|
|
3566
|
+
|
|
3567
|
+
const graphieUnitsPerUnit = options.pixelsPerUnit / graphie.scale[0];
|
|
3568
|
+
const graphieUnitsHeight = height / graphie.scale[0];
|
|
3569
|
+
|
|
3570
|
+
const rightTop = kvector.add(leftBottom, [
|
|
3571
|
+
options.units * graphieUnitsPerUnit,
|
|
3572
|
+
graphieUnitsHeight,
|
|
3573
|
+
]);
|
|
3574
|
+
|
|
3575
|
+
const tickHeight = 1.0;
|
|
3576
|
+
let tickHeightMap;
|
|
3577
|
+
|
|
3578
|
+
if (options.ticksPerUnit === 10) {
|
|
3579
|
+
// decimal, as on a centimeter ruler
|
|
3580
|
+
tickHeightMap = {
|
|
3581
|
+
// $FlowFixMe[unsupported-syntax]
|
|
3582
|
+
10: tickHeight,
|
|
3583
|
+
// $FlowFixMe[unsupported-syntax]
|
|
3584
|
+
5: tickHeight * 0.55,
|
|
3585
|
+
// $FlowFixMe[unsupported-syntax]
|
|
3586
|
+
1: tickHeight * 0.35,
|
|
3587
|
+
};
|
|
3588
|
+
} else {
|
|
3589
|
+
const sizes = [1, 0.6, 0.45, 0.3];
|
|
3590
|
+
|
|
3591
|
+
tickHeightMap = {};
|
|
3592
|
+
for (let i = options.ticksPerUnit; i >= 1; i /= 2) {
|
|
3593
|
+
tickHeightMap[i] = tickHeight * (sizes.shift() || 0.2);
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
|
|
3597
|
+
const tickFrequencies = _.keys(tickHeightMap).sort(function (a, b) {
|
|
3598
|
+
// @ts-expect-error [FEI-5003] - TS2362 - The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. | TS2363 - The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
|
|
3599
|
+
return b - a;
|
|
3600
|
+
});
|
|
3601
|
+
|
|
3602
|
+
function getTickHeight(i: any): number {
|
|
3603
|
+
for (let k = 0; k < tickFrequencies.length; k++) {
|
|
3604
|
+
const key = tickFrequencies[k];
|
|
3605
|
+
// @ts-expect-error [FEI-5003] - TS2363 - The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
|
|
3606
|
+
if (i % key === 0) {
|
|
3607
|
+
return tickHeightMap[key];
|
|
3608
|
+
}
|
|
3609
|
+
}
|
|
3610
|
+
return 0;
|
|
3611
|
+
}
|
|
3612
|
+
|
|
3613
|
+
const left = leftBottom[0];
|
|
3614
|
+
const bottom = leftBottom[1];
|
|
3615
|
+
const right = rightTop[0];
|
|
3616
|
+
const top = rightTop[1];
|
|
3617
|
+
|
|
3618
|
+
const numTicks = options.units * options.ticksPerUnit + 1;
|
|
3619
|
+
|
|
3620
|
+
const set = graphie.raphael.set();
|
|
3621
|
+
|
|
3622
|
+
const px = 1 / graphie.scale[0];
|
|
3623
|
+
set.push(graphie.line([left - px, bottom], [right + px, bottom], bold));
|
|
3624
|
+
set.push(graphie.line([left - px, top], [right + px, top], bold));
|
|
3625
|
+
|
|
3626
|
+
_.times(numTicks, function (i) {
|
|
3627
|
+
const n = i / options.ticksPerUnit;
|
|
3628
|
+
const x = left + n * graphieUnitsPerUnit;
|
|
3629
|
+
const height = getTickHeight(i) * graphieUnitsHeight;
|
|
3630
|
+
|
|
3631
|
+
const style = i === 0 || i === numTicks - 1 ? bold : light;
|
|
3632
|
+
set.push(graphie.line([x, bottom], [x, bottom + height], style));
|
|
3633
|
+
|
|
3634
|
+
if (n % 1 === 0) {
|
|
3635
|
+
const coord = graphie.scalePoint([x, top]);
|
|
3636
|
+
let text;
|
|
3637
|
+
let offset;
|
|
3638
|
+
|
|
3639
|
+
if (n === 0) {
|
|
3640
|
+
// Unit label
|
|
3641
|
+
text = options.label;
|
|
3642
|
+
offset =
|
|
3643
|
+
{
|
|
3644
|
+
mm: 13,
|
|
3645
|
+
cm: 11,
|
|
3646
|
+
m: 8,
|
|
3647
|
+
km: 11,
|
|
3648
|
+
in: 8,
|
|
3649
|
+
ft: 8,
|
|
3650
|
+
yd: 10,
|
|
3651
|
+
mi: 10,
|
|
3652
|
+
}[text] || 3 * text.toString().length;
|
|
3653
|
+
} else {
|
|
3654
|
+
// Tick label
|
|
3655
|
+
text = n;
|
|
3656
|
+
offset = -3 * (n.toString().length + 1);
|
|
3657
|
+
}
|
|
3658
|
+
const label = graphie.raphael.text(
|
|
3659
|
+
coord[0] + offset,
|
|
3660
|
+
coord[1] + 10,
|
|
3661
|
+
text,
|
|
3662
|
+
);
|
|
3663
|
+
label.attr({
|
|
3664
|
+
"font-family": "KaTeX_Main",
|
|
3665
|
+
"font-size": "12px",
|
|
3666
|
+
color: "#444",
|
|
3667
|
+
});
|
|
3668
|
+
set.push(label);
|
|
3669
|
+
}
|
|
3670
|
+
});
|
|
3671
|
+
|
|
3672
|
+
const mouseTarget = graphie.mouselayer.path(
|
|
3673
|
+
GraphUtils.svgPath([
|
|
3674
|
+
leftBottom,
|
|
3675
|
+
[left, top],
|
|
3676
|
+
rightTop,
|
|
3677
|
+
[right, bottom],
|
|
3678
|
+
/* closed */ true,
|
|
3679
|
+
]),
|
|
3680
|
+
);
|
|
3681
|
+
mouseTarget.attr({
|
|
3682
|
+
fill: "#000",
|
|
3683
|
+
opacity: 0,
|
|
3684
|
+
stroke: "#000",
|
|
3685
|
+
"stroke-width": 2,
|
|
3686
|
+
});
|
|
3687
|
+
set.push(mouseTarget);
|
|
3688
|
+
|
|
3689
|
+
// Prevent the page from scrolling when we grab and drag the ruler on a
|
|
3690
|
+
// mobile device.
|
|
3691
|
+
mouseTarget.node.addEventListener(
|
|
3692
|
+
"touchstart",
|
|
3693
|
+
function (event) {
|
|
3694
|
+
event.preventDefault();
|
|
3695
|
+
},
|
|
3696
|
+
{passive: false},
|
|
3697
|
+
);
|
|
3698
|
+
|
|
3699
|
+
const setNodes = $.map(set, function (el) {
|
|
3700
|
+
return el.node;
|
|
3701
|
+
});
|
|
3702
|
+
$(setNodes).css("cursor", "move");
|
|
3703
|
+
|
|
3704
|
+
$(setNodes).bind("vmousedown", function (event) {
|
|
3705
|
+
event.preventDefault();
|
|
3706
|
+
let startx =
|
|
3707
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
3708
|
+
event.pageX - $(graphie.raphael.canvas.parentNode).offset().left;
|
|
3709
|
+
let starty =
|
|
3710
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
3711
|
+
event.pageY - $(graphie.raphael.canvas.parentNode).offset().top;
|
|
3712
|
+
|
|
3713
|
+
$(document).bind("vmousemove.ruler", function (event) {
|
|
3714
|
+
let mouseX =
|
|
3715
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
3716
|
+
event.pageX -
|
|
3717
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'.
|
|
3718
|
+
$(graphie.raphael.canvas.parentNode).offset().left;
|
|
3719
|
+
let mouseY =
|
|
3720
|
+
// @ts-expect-error [FEI-5003] - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
3721
|
+
event.pageY - $(graphie.raphael.canvas.parentNode).offset().top;
|
|
3722
|
+
// can't go beyond 10 pixels from the edge
|
|
3723
|
+
mouseX = Math.max(10, Math.min(graphie.xpixels - 10, mouseX));
|
|
3724
|
+
mouseY = Math.max(10, Math.min(graphie.ypixels - 10, mouseY));
|
|
3725
|
+
|
|
3726
|
+
const dx = mouseX - startx;
|
|
3727
|
+
const dy = mouseY - starty;
|
|
3728
|
+
|
|
3729
|
+
set.translate(dx, dy);
|
|
3730
|
+
leftBottomHandle.setCoord([
|
|
3731
|
+
leftBottomHandle.coord[0] + dx / graphie.scale[0],
|
|
3732
|
+
leftBottomHandle.coord[1] - dy / graphie.scale[1],
|
|
3733
|
+
]);
|
|
3734
|
+
rightBottomHandle.setCoord([
|
|
3735
|
+
rightBottomHandle.coord[0] + dx / graphie.scale[0],
|
|
3736
|
+
rightBottomHandle.coord[1] - dy / graphie.scale[1],
|
|
3737
|
+
]);
|
|
3738
|
+
|
|
3739
|
+
startx = mouseX;
|
|
3740
|
+
starty = mouseY;
|
|
3741
|
+
});
|
|
3742
|
+
|
|
3743
|
+
$(document).one("vmouseup", function (event) {
|
|
3744
|
+
$(document).unbind("vmousemove.ruler");
|
|
3745
|
+
});
|
|
3746
|
+
});
|
|
3747
|
+
|
|
3748
|
+
const leftBottomHandle = graphie.addMovablePoint({
|
|
3749
|
+
coord: leftBottom,
|
|
3750
|
+
normalStyle: {
|
|
3751
|
+
fill: KhanColors.INTERACTIVE,
|
|
3752
|
+
"fill-opacity": 0,
|
|
3753
|
+
stroke: KhanColors.INTERACTIVE,
|
|
3754
|
+
},
|
|
3755
|
+
highlightStyle: {
|
|
3756
|
+
fill: KhanColors.INTERACTING,
|
|
3757
|
+
"fill-opacity": 0.1,
|
|
3758
|
+
stroke: KhanColors.INTERACTING,
|
|
3759
|
+
},
|
|
3760
|
+
pointSize: 6, // or 8 maybe?
|
|
3761
|
+
onMove: function (x, y) {
|
|
3762
|
+
const dy = rightBottomHandle.coord[1] - y;
|
|
3763
|
+
const dx = rightBottomHandle.coord[0] - x;
|
|
3764
|
+
const angle = (Math.atan2(dy, dx) * 180) / Math.PI;
|
|
3765
|
+
const center = kvector.scale(
|
|
3766
|
+
kvector.add([x, y], rightBottomHandle.coord),
|
|
3767
|
+
0.5,
|
|
3768
|
+
);
|
|
3769
|
+
const scaledCenter = graphie.scalePoint(center);
|
|
3770
|
+
const oldCenter = kvector.scale(
|
|
3771
|
+
kvector.add(leftBottomHandle.coord, rightBottomHandle.coord),
|
|
3772
|
+
0.5,
|
|
3773
|
+
);
|
|
3774
|
+
const scaledOldCenter = graphie.scalePoint(oldCenter);
|
|
3775
|
+
const diff = kvector.subtract(scaledCenter, scaledOldCenter);
|
|
3776
|
+
set.rotate(-angle, scaledOldCenter[0], scaledOldCenter[1]);
|
|
3777
|
+
set.translate(diff[0], diff[1]);
|
|
3778
|
+
},
|
|
3779
|
+
});
|
|
3780
|
+
const rightBottomHandle = graphie.addMovablePoint({
|
|
3781
|
+
coord: [right, bottom],
|
|
3782
|
+
normalStyle: {
|
|
3783
|
+
fill: KhanColors.INTERACTIVE,
|
|
3784
|
+
"fill-opacity": 0,
|
|
3785
|
+
stroke: KhanColors.INTERACTIVE,
|
|
3786
|
+
},
|
|
3787
|
+
highlightStyle: {
|
|
3788
|
+
fill: KhanColors.INTERACTING,
|
|
3789
|
+
"fill-opacity": 0.1,
|
|
3790
|
+
stroke: KhanColors.INTERACTING,
|
|
3791
|
+
},
|
|
3792
|
+
pointSize: 6, // or 8 maybe?
|
|
3793
|
+
onMove: function (x, y) {
|
|
3794
|
+
const dy = y - leftBottomHandle.coord[1];
|
|
3795
|
+
const dx = x - leftBottomHandle.coord[0];
|
|
3796
|
+
const angle = (Math.atan2(dy, dx) * 180) / Math.PI;
|
|
3797
|
+
const center = kvector.scale(
|
|
3798
|
+
kvector.add([x, y], leftBottomHandle.coord),
|
|
3799
|
+
0.5,
|
|
3800
|
+
);
|
|
3801
|
+
const scaledCenter = graphie.scalePoint(center);
|
|
3802
|
+
const oldCenter = kvector.scale(
|
|
3803
|
+
kvector.add(leftBottomHandle.coord, rightBottomHandle.coord),
|
|
3804
|
+
0.5,
|
|
3805
|
+
);
|
|
3806
|
+
const scaledOldCenter = graphie.scalePoint(oldCenter);
|
|
3807
|
+
const diff = kvector.subtract(scaledCenter, scaledOldCenter);
|
|
3808
|
+
set.rotate(-angle, scaledOldCenter[0], scaledOldCenter[1]);
|
|
3809
|
+
set.translate(diff[0], diff[1]);
|
|
3810
|
+
},
|
|
3811
|
+
});
|
|
3812
|
+
|
|
3813
|
+
// Make each handle rotate the ruler about the other one
|
|
3814
|
+
leftBottomHandle.constraints.fixedDistance.dist = width / graphie.scale[0];
|
|
3815
|
+
leftBottomHandle.constraints.fixedDistance.point = rightBottomHandle;
|
|
3816
|
+
rightBottomHandle.constraints.fixedDistance.dist = width / graphie.scale[0];
|
|
3817
|
+
rightBottomHandle.constraints.fixedDistance.point = leftBottomHandle;
|
|
3818
|
+
|
|
3819
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3820
|
+
this.remove = function () {
|
|
3821
|
+
set.remove();
|
|
3822
|
+
leftBottomHandle.remove();
|
|
3823
|
+
rightBottomHandle.remove();
|
|
3824
|
+
};
|
|
3825
|
+
|
|
3826
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3827
|
+
return this;
|
|
3828
|
+
}
|
|
3829
|
+
|
|
3830
|
+
function MovableAngle(graphie: any, options: any) {
|
|
3831
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3832
|
+
this.graphie = graphie;
|
|
3833
|
+
|
|
3834
|
+
// TODO(alex): Move standard colors from math.js to somewhere else
|
|
3835
|
+
// so that they are available when this file is first parsed
|
|
3836
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3837
|
+
_.extend(this, options);
|
|
3838
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3839
|
+
_.defaults(this, {
|
|
3840
|
+
normalStyle: {
|
|
3841
|
+
stroke: KhanColors.INTERACTIVE,
|
|
3842
|
+
"stroke-width": 2,
|
|
3843
|
+
fill: KhanColors.INTERACTIVE,
|
|
3844
|
+
},
|
|
3845
|
+
highlightStyle: {
|
|
3846
|
+
stroke: KhanColors.INTERACTING,
|
|
3847
|
+
"stroke-width": 2,
|
|
3848
|
+
fill: KhanColors.INTERACTING,
|
|
3849
|
+
},
|
|
3850
|
+
labelStyle: {
|
|
3851
|
+
stroke: KhanColors.DYNAMIC,
|
|
3852
|
+
"stroke-width": 1,
|
|
3853
|
+
color: KhanColors.DYNAMIC,
|
|
3854
|
+
},
|
|
3855
|
+
angleStyle: {
|
|
3856
|
+
stroke: KhanColors.DYNAMIC,
|
|
3857
|
+
"stroke-width": 1,
|
|
3858
|
+
color: KhanColors.DYNAMIC,
|
|
3859
|
+
},
|
|
3860
|
+
allowReflex: true, // not on MovableAngle.prototype so that
|
|
3861
|
+
// it is not overridden by undefined
|
|
3862
|
+
});
|
|
3863
|
+
|
|
3864
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3865
|
+
if (!this.points || this.points.length !== 3) {
|
|
3866
|
+
throw new PerseusError(
|
|
3867
|
+
"MovableAngle requires 3 points",
|
|
3868
|
+
Errors.InvalidInput,
|
|
3869
|
+
);
|
|
3870
|
+
}
|
|
3871
|
+
|
|
3872
|
+
// Handle coordinates that are not MovablePoints (i.e. [2, 4])
|
|
3873
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3874
|
+
this.points = _.map(
|
|
3875
|
+
options.points,
|
|
3876
|
+
function (point) {
|
|
3877
|
+
if (_.isArray(point)) {
|
|
3878
|
+
return graphie.addMovablePoint({
|
|
3879
|
+
coord: point,
|
|
3880
|
+
visible: false,
|
|
3881
|
+
constraints: {
|
|
3882
|
+
fixed: true,
|
|
3883
|
+
},
|
|
3884
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3885
|
+
normalStyle: this.normalStyle,
|
|
3886
|
+
});
|
|
3887
|
+
}
|
|
3888
|
+
return point;
|
|
3889
|
+
},
|
|
3890
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3891
|
+
this,
|
|
3892
|
+
);
|
|
3893
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3894
|
+
this.coords = _.pluck(this.points, "coord");
|
|
3895
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3896
|
+
if (this.reflex == null) {
|
|
3897
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3898
|
+
if (this.allowReflex) {
|
|
3899
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3900
|
+
this.reflex = this._getClockwiseAngle(this.coords) > 180;
|
|
3901
|
+
} else {
|
|
3902
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3903
|
+
this.reflex = false;
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3907
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3908
|
+
this.rays = _.map(
|
|
3909
|
+
[0, 2],
|
|
3910
|
+
function (i) {
|
|
3911
|
+
return graphie.addMovableLineSegment({
|
|
3912
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3913
|
+
pointA: this.points[1],
|
|
3914
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3915
|
+
pointZ: this.points[i],
|
|
3916
|
+
fixed: true,
|
|
3917
|
+
extendRay: true,
|
|
3918
|
+
});
|
|
3919
|
+
},
|
|
3920
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3921
|
+
this,
|
|
3922
|
+
);
|
|
3923
|
+
|
|
3924
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3925
|
+
this.temp = [];
|
|
3926
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3927
|
+
this.labeledAngle = graphie.label([0, 0], "", "center", this.labelStyle);
|
|
3928
|
+
|
|
3929
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3930
|
+
if (!this.fixed) {
|
|
3931
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3932
|
+
this.addMoveHandlers();
|
|
3933
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3934
|
+
this.addHighlightHandlers();
|
|
3935
|
+
}
|
|
3936
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
3937
|
+
this.update();
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3940
|
+
_.extend(MovableAngle.prototype, {
|
|
3941
|
+
points: [],
|
|
3942
|
+
snapDegrees: 0,
|
|
3943
|
+
snapOffsetDeg: 0,
|
|
3944
|
+
angleLabel: "",
|
|
3945
|
+
numArcs: 1,
|
|
3946
|
+
pushOut: 0,
|
|
3947
|
+
fixed: false,
|
|
3948
|
+
|
|
3949
|
+
addMoveHandlers: function () {
|
|
3950
|
+
const graphie = this.graphie;
|
|
3951
|
+
|
|
3952
|
+
function tooClose(point1: Array<never>, point2: Array<never>) {
|
|
3953
|
+
const safeDistance = 30;
|
|
3954
|
+
const distance = GraphUtils.getDistance(
|
|
3955
|
+
graphie.scalePoint(point1),
|
|
3956
|
+
graphie.scalePoint(point2),
|
|
3957
|
+
);
|
|
3958
|
+
return distance < safeDistance;
|
|
3959
|
+
}
|
|
3960
|
+
|
|
3961
|
+
const points = this.points;
|
|
3962
|
+
|
|
3963
|
+
// Drag the vertex to move the entire angle
|
|
3964
|
+
points[1].onMove = function (x, y: any) {
|
|
3965
|
+
const oldVertex = points[1].coord;
|
|
3966
|
+
const newVertex = [x, y];
|
|
3967
|
+
const delta = addPoints(newVertex, reverseVector(oldVertex));
|
|
3968
|
+
|
|
3969
|
+
let valid = true;
|
|
3970
|
+
const newPoints: Record<string, any> = {};
|
|
3971
|
+
_.each([0, 2], function (i) {
|
|
3972
|
+
const oldPoint = points[i].coord;
|
|
3973
|
+
let newPoint = addPoints(oldPoint, delta);
|
|
3974
|
+
|
|
3975
|
+
let angle = GraphUtils.findAngle(newVertex, newPoint);
|
|
3976
|
+
angle *= Math.PI / 180;
|
|
3977
|
+
newPoint = graphie.constrainToBoundsOnAngle(
|
|
3978
|
+
newPoint,
|
|
3979
|
+
10,
|
|
3980
|
+
angle,
|
|
3981
|
+
);
|
|
3982
|
+
newPoints[i] = newPoint;
|
|
3983
|
+
|
|
3984
|
+
// @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'any[]' is not assignable to parameter of type 'never[]'.
|
|
3985
|
+
if (tooClose(newVertex, newPoint)) {
|
|
3986
|
+
valid = false;
|
|
3987
|
+
}
|
|
3988
|
+
});
|
|
3989
|
+
|
|
3990
|
+
// Only move points if all new positions are valid
|
|
3991
|
+
if (valid) {
|
|
3992
|
+
_.each(newPoints, function (newPoint, i) {
|
|
3993
|
+
points[i].setCoord(newPoint);
|
|
3994
|
+
});
|
|
3995
|
+
}
|
|
3996
|
+
return valid;
|
|
3997
|
+
};
|
|
3998
|
+
|
|
3999
|
+
const snap = this.snapDegrees;
|
|
4000
|
+
const snapOffset = this.snapOffsetDeg;
|
|
4001
|
+
|
|
4002
|
+
// Drag ray control points to move each ray individually
|
|
4003
|
+
_.each([0, 2], function (i) {
|
|
4004
|
+
points[i].onMove = function (x: any, y) {
|
|
4005
|
+
const newPoint = [x, y];
|
|
4006
|
+
const vertex = points[1].coord;
|
|
4007
|
+
|
|
4008
|
+
// @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'any[]' is not assignable to parameter of type 'never[]'.
|
|
4009
|
+
if (tooClose(vertex, newPoint)) {
|
|
4010
|
+
return false;
|
|
4011
|
+
}
|
|
4012
|
+
if (snap) {
|
|
4013
|
+
let angle = GraphUtils.findAngle(newPoint, vertex);
|
|
4014
|
+
angle =
|
|
4015
|
+
Math.round((angle - snapOffset) / snap) * snap +
|
|
4016
|
+
snapOffset;
|
|
4017
|
+
const distance = GraphUtils.getDistance(newPoint, vertex);
|
|
4018
|
+
return addPoints(vertex, graphie.polar(distance, angle));
|
|
4019
|
+
}
|
|
4020
|
+
return true;
|
|
4021
|
+
};
|
|
4022
|
+
});
|
|
4023
|
+
|
|
4024
|
+
// Expose only a single move event
|
|
4025
|
+
$(points).on(
|
|
4026
|
+
"move",
|
|
4027
|
+
function () {
|
|
4028
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4029
|
+
this.update();
|
|
4030
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4031
|
+
$(this).trigger("move");
|
|
4032
|
+
}.bind(this),
|
|
4033
|
+
);
|
|
4034
|
+
},
|
|
4035
|
+
|
|
4036
|
+
addHighlightHandlers: function () {
|
|
4037
|
+
const vertex = this.points[1];
|
|
4038
|
+
|
|
4039
|
+
vertex.onHighlight = function () {
|
|
4040
|
+
_.each(
|
|
4041
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4042
|
+
this.points,
|
|
4043
|
+
function (point) {
|
|
4044
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4045
|
+
point.visibleShape.animate(this.highlightStyle, 50);
|
|
4046
|
+
},
|
|
4047
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4048
|
+
this,
|
|
4049
|
+
);
|
|
4050
|
+
_.each(
|
|
4051
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4052
|
+
this.rays,
|
|
4053
|
+
function (ray) {
|
|
4054
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4055
|
+
ray.visibleLine.animate(this.highlightStyle, 50);
|
|
4056
|
+
ray.arrowStyle = _.extend({}, ray.arrowStyle, {
|
|
4057
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4058
|
+
color: this.highlightStyle.stroke,
|
|
4059
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4060
|
+
stroke: this.highlightStyle.stroke,
|
|
4061
|
+
});
|
|
4062
|
+
},
|
|
4063
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4064
|
+
this,
|
|
4065
|
+
);
|
|
4066
|
+
|
|
4067
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4068
|
+
this.angleStyle = _.extend({}, this.angleStyle, {
|
|
4069
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4070
|
+
color: this.highlightStyle.stroke,
|
|
4071
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4072
|
+
stroke: this.highlightStyle.stroke,
|
|
4073
|
+
});
|
|
4074
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4075
|
+
this.update();
|
|
4076
|
+
}.bind(this);
|
|
4077
|
+
|
|
4078
|
+
vertex.onUnhighlight = function () {
|
|
4079
|
+
_.each(
|
|
4080
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4081
|
+
this.points,
|
|
4082
|
+
function (point) {
|
|
4083
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4084
|
+
point.visibleShape.animate(this.normalStyle, 50);
|
|
4085
|
+
},
|
|
4086
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4087
|
+
this,
|
|
4088
|
+
);
|
|
4089
|
+
_.each(
|
|
4090
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4091
|
+
this.rays,
|
|
4092
|
+
function (ray) {
|
|
4093
|
+
ray.visibleLine.animate(ray.normalStyle, 50);
|
|
4094
|
+
ray.arrowStyle = _.extend({}, ray.arrowStyle, {
|
|
4095
|
+
color: ray.normalStyle.stroke,
|
|
4096
|
+
stroke: ray.normalStyle.stroke,
|
|
4097
|
+
});
|
|
4098
|
+
},
|
|
4099
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4100
|
+
this,
|
|
4101
|
+
);
|
|
4102
|
+
|
|
4103
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation. | TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4104
|
+
this.angleStyle = _.extend({}, this.angleStyle, {
|
|
4105
|
+
color: KhanColors.DYNAMIC,
|
|
4106
|
+
stroke: KhanColors.DYNAMIC,
|
|
4107
|
+
});
|
|
4108
|
+
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
4109
|
+
this.update();
|
|
4110
|
+
}.bind(this);
|
|
4111
|
+
},
|
|
4112
|
+
|
|
4113
|
+
/**
|
|
4114
|
+
* Returns the angle in [0, 360) degrees created by the
|
|
4115
|
+
* coords when interpreted in a clockwise direction.
|
|
4116
|
+
*/
|
|
4117
|
+
_getClockwiseAngle: function (coords) {
|
|
4118
|
+
const rawAngle = GraphUtils.findAngle(
|
|
4119
|
+
// The order of these is "weird" to match what a clockwise
|
|
4120
|
+
// order is in graphie.labelAngle
|
|
4121
|
+
coords[2], // from the second point
|
|
4122
|
+
coords[0], // clockwise to the first point
|
|
4123
|
+
coords[1], // the vertex parameter is last
|
|
4124
|
+
);
|
|
4125
|
+
const clockwiseAngle = rawAngle + (360 % 360);
|
|
4126
|
+
|
|
4127
|
+
return clockwiseAngle;
|
|
4128
|
+
},
|
|
4129
|
+
|
|
4130
|
+
isReflex: function () {
|
|
4131
|
+
return this.reflex;
|
|
4132
|
+
},
|
|
4133
|
+
|
|
4134
|
+
isClockwise: function () {
|
|
4135
|
+
const clockwiseReflexive = this._getClockwiseAngle(this.coords) > 180;
|
|
4136
|
+
return clockwiseReflexive === this.reflex;
|
|
4137
|
+
},
|
|
4138
|
+
|
|
4139
|
+
getClockwiseCoords: function () {
|
|
4140
|
+
if (this.isClockwise()) {
|
|
4141
|
+
return _.clone(this.coords);
|
|
4142
|
+
}
|
|
4143
|
+
return _.clone(this.coords).reverse();
|
|
4144
|
+
},
|
|
4145
|
+
|
|
4146
|
+
update: function (shouldChangeReflexivity) {
|
|
4147
|
+
const prevCoords = this.coords;
|
|
4148
|
+
this.coords = _.pluck(this.points, "coord");
|
|
4149
|
+
|
|
4150
|
+
// Update lines
|
|
4151
|
+
_.invoke(this.points, "updateLineEnds");
|
|
4152
|
+
|
|
4153
|
+
const prevAngle = this._getClockwiseAngle(prevCoords);
|
|
4154
|
+
const angle = this._getClockwiseAngle(this.coords);
|
|
4155
|
+
const prevClockwiseReflexive = prevAngle > 180;
|
|
4156
|
+
const clockwiseReflexive = angle > 180;
|
|
4157
|
+
|
|
4158
|
+
if (this.allowReflex) {
|
|
4159
|
+
if (shouldChangeReflexivity == null) {
|
|
4160
|
+
shouldChangeReflexivity =
|
|
4161
|
+
prevClockwiseReflexive !== clockwiseReflexive &&
|
|
4162
|
+
Math.abs(angle - prevAngle) < 180;
|
|
4163
|
+
}
|
|
4164
|
+
|
|
4165
|
+
if (shouldChangeReflexivity) {
|
|
4166
|
+
this.reflex = !this.reflex;
|
|
4167
|
+
}
|
|
4168
|
+
}
|
|
4169
|
+
|
|
4170
|
+
_.invoke(this.temp, "remove");
|
|
4171
|
+
this.temp = this.graphie.labelAngle({
|
|
4172
|
+
point1: this.coords[0],
|
|
4173
|
+
vertex: this.coords[1],
|
|
4174
|
+
point3: this.coords[2],
|
|
4175
|
+
label: this.labeledAngle,
|
|
4176
|
+
text: this.angleLabel,
|
|
4177
|
+
numArcs: this.numArcs,
|
|
4178
|
+
pushOut: this.pushOut,
|
|
4179
|
+
clockwise: this.reflex === clockwiseReflexive,
|
|
4180
|
+
style: this.angleStyle,
|
|
4181
|
+
});
|
|
4182
|
+
},
|
|
4183
|
+
|
|
4184
|
+
remove: function () {
|
|
4185
|
+
_.invoke(this.rays, "remove");
|
|
4186
|
+
_.invoke(this.temp, "remove");
|
|
4187
|
+
this.labeledAngle.remove();
|
|
4188
|
+
},
|
|
4189
|
+
});
|
|
4190
|
+
|
|
4191
|
+
export default InteractiveUtils;
|