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