@khanacademy/perseus 19.0.0 → 19.1.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/CHANGELOG.md +37 -0
- package/dist/es/index.css +209 -104
- package/dist/es/index.js +806 -877
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +209 -104
- package/dist/index.d.ts +3 -2
- package/dist/index.js +849 -920
- package/dist/index.js.map +1 -1
- package/dist/mixins/changeable.d.ts +1 -1
- package/dist/perseus-api.d.ts +1 -0
- package/dist/renderer.d.ts +2 -4
- package/dist/server-item-renderer.d.ts +1 -0
- package/dist/types.d.ts +4 -0
- package/dist/util/geometry.d.ts +2 -0
- package/dist/util/graphie.d.ts +107 -1
- package/dist/util/math.d.ts +2 -0
- package/dist/util/parse-perseus-json.d.ts +3 -3
- package/dist/widgets/expression.d.ts +6 -0
- package/dist/widgets/label-image.d.ts +2 -0
- package/package.json +3 -4
- package/src/__testdata__/renderer.testdata.ts +152 -0
- package/src/__tests__/renderer.test.tsx +17 -0
- package/src/components/math-input.tsx +4 -6
- package/src/index.ts +8 -3
- package/src/mixins/changeable.ts +1 -1
- package/src/renderer.tsx +14 -8
- package/src/types.ts +9 -0
- package/src/util/geometry.test.ts +75 -0
- package/src/util/geometry.ts +19 -0
- package/src/util/graphie.test.ts +1291 -0
- package/src/util/graphie.ts +832 -784
- package/src/util/interactive.ts +23 -51
- package/src/util/math.test.ts +11 -0
- package/src/util/math.ts +8 -0
- package/src/util/parse-perseus-json.test.ts +3 -3
- package/src/util/parse-perseus-json.ts +4 -4
- package/src/widgets/__tests__/__snapshots__/input-number.test.ts.snap +4 -2
- package/src/widgets/__tests__/__snapshots__/numeric-input.test.ts.snap +4 -2
- package/src/widgets/__tests__/expression-mobile.test.tsx +8 -5
- package/src/widgets/expression.tsx +1 -23
- package/src/widgets/label-image.tsx +0 -1
- package/tsconfig-build.tsbuildinfo +1 -1
package/dist/es/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import PropTypes from 'prop-types';
|
|
|
19
19
|
import * as ReactDOM from 'react-dom';
|
|
20
20
|
import ReactDOM__default from 'react-dom';
|
|
21
21
|
import { number, point, vector as vector$1, line } from '@khanacademy/kmath';
|
|
22
|
+
import { entries, keys } from '@khanacademy/wonder-stuff-core';
|
|
22
23
|
import { CircularSpinner } from '@khanacademy/wonder-blocks-progress-spinner';
|
|
23
24
|
import SimpleMarkdown from '@khanacademy/simple-markdown';
|
|
24
25
|
import { pureMarkdownRules } from '@khanacademy/pure-markdown';
|
|
@@ -35,7 +36,6 @@ import { Popper } from 'react-popper';
|
|
|
35
36
|
import createReactClass from 'create-react-class';
|
|
36
37
|
import Link from '@khanacademy/wonder-blocks-link';
|
|
37
38
|
import { addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
|
|
38
|
-
import { keys } from '@khanacademy/wonder-stuff-core';
|
|
39
39
|
|
|
40
40
|
function _extends() {
|
|
41
41
|
_extends = Object.assign ? Object.assign.bind() : function (target) {
|
|
@@ -140,9 +140,9 @@ class MathInput extends React.Component {
|
|
|
140
140
|
// We do this by modifying internal MathQuill state
|
|
141
141
|
// directly, instead of waiting for `.latex()` to be
|
|
142
142
|
// called in `componentDidUpdate()`.
|
|
143
|
-
const left = mathField.
|
|
143
|
+
const left = mathField.cursor()[mathQuillInstance.L];
|
|
144
144
|
if (left && left.ctrlSeq === "\\cdot ") {
|
|
145
|
-
mathField.
|
|
145
|
+
mathField.controller().backspace();
|
|
146
146
|
mathField.cmd("\\times");
|
|
147
147
|
}
|
|
148
148
|
} else {
|
|
@@ -1202,6 +1202,12 @@ const KhanMath = {
|
|
|
1202
1202
|
return String(number$1);
|
|
1203
1203
|
}
|
|
1204
1204
|
};
|
|
1205
|
+
function sum$1(array) {
|
|
1206
|
+
return array.reduce(add$3, 0);
|
|
1207
|
+
}
|
|
1208
|
+
function add$3(a, b) {
|
|
1209
|
+
return a + b;
|
|
1210
|
+
}
|
|
1205
1211
|
|
|
1206
1212
|
/* eslint-disable no-useless-escape */
|
|
1207
1213
|
const MAXERROR_EPSILON = Math.pow(2, -42);
|
|
@@ -1894,27 +1900,6 @@ const KhanAnswerTypes = {
|
|
|
1894
1900
|
const _excluded$4 = ["scoring"];
|
|
1895
1901
|
const ERROR_TITLE = i18n._("Oops!");
|
|
1896
1902
|
const ERROR_MESSAGE = i18n._("Sorry, I don't understand that!");
|
|
1897
|
-
const insertBraces = value => {
|
|
1898
|
-
// HACK(alex): Make sure that all LaTeX super/subscripts are wrapped
|
|
1899
|
-
// in curly braces to avoid the mismatch between KAS and LaTeX sup/sub
|
|
1900
|
-
// parsing.
|
|
1901
|
-
//
|
|
1902
|
-
// What exactly is this mismatch? Due to its heritage of parsing plain
|
|
1903
|
-
// text math from <OldExpression />, KAS parses "x^12" as x^(12).
|
|
1904
|
-
// This is both generally what the user expects to happen, and is
|
|
1905
|
-
// consistent with other computer algebra systems. It is NOT
|
|
1906
|
-
// consistent with LaTeX however, where x^12 is equivalent to x^{1}2.
|
|
1907
|
-
//
|
|
1908
|
-
// Since the only LaTeX we parse comes from MathQuill, this wouldn't
|
|
1909
|
-
// be a problem if MathQuill just always gave us the latter version
|
|
1910
|
-
// (with explicit braces). However, instead it always gives the former.
|
|
1911
|
-
// This behavior is baked in pretty deep; my naive attempts at changing
|
|
1912
|
-
// it triggered all sorts of confusing errors. So instead we just make
|
|
1913
|
-
// sure to add in any missing braces before grading MathQuill input.
|
|
1914
|
-
//
|
|
1915
|
-
// TODO(alex): Properly hack MathQuill to always use explicit braces.
|
|
1916
|
-
return value.replace(/([_^])([^{])/g, "$1{$2}");
|
|
1917
|
-
};
|
|
1918
1903
|
const anglicizeOperators = tex => {
|
|
1919
1904
|
// sen is used instead of sin in some languages, e.g. Portuguese.
|
|
1920
1905
|
// To ensure that answers in various languages are graded correctly, we
|
|
@@ -1922,7 +1907,7 @@ const anglicizeOperators = tex => {
|
|
|
1922
1907
|
return tex.replace(/\\operatorname{sen}/g, "\\sin ");
|
|
1923
1908
|
};
|
|
1924
1909
|
const normalizeTex = tex => {
|
|
1925
|
-
return anglicizeOperators(
|
|
1910
|
+
return anglicizeOperators(tex);
|
|
1926
1911
|
};
|
|
1927
1912
|
const deriveKeypadVersion = apiOptions => {
|
|
1928
1913
|
// We can derive which version of the keypad is in use. This is
|
|
@@ -8700,54 +8685,6 @@ function polar(r, th) {
|
|
|
8700
8685
|
th = th * Math.PI / 180;
|
|
8701
8686
|
return [r[0] * Math.cos(th), r[1] * Math.sin(th)];
|
|
8702
8687
|
}
|
|
8703
|
-
const GraphUtils = {
|
|
8704
|
-
unscaledSvgPath: function (points) {
|
|
8705
|
-
// If this is an empty closed path, return "" instead of "z", which
|
|
8706
|
-
// would give an error
|
|
8707
|
-
if (points[0] === true) {
|
|
8708
|
-
return "";
|
|
8709
|
-
}
|
|
8710
|
-
return $.map(points, function (point, i) {
|
|
8711
|
-
if (point === true) {
|
|
8712
|
-
return "z";
|
|
8713
|
-
}
|
|
8714
|
-
return (i === 0 ? "M" : "L") + point[0] + " " + point[1];
|
|
8715
|
-
}).join("");
|
|
8716
|
-
},
|
|
8717
|
-
getDistance: function (point1, point2) {
|
|
8718
|
-
return point.distanceToPoint(point1, point2);
|
|
8719
|
-
},
|
|
8720
|
-
/**
|
|
8721
|
-
* Return the difference between two sets of coordinates
|
|
8722
|
-
*/
|
|
8723
|
-
coordDiff: function (startCoord, endCoord) {
|
|
8724
|
-
return _.map(endCoord, function (val, i) {
|
|
8725
|
-
return endCoord[i] - startCoord[i];
|
|
8726
|
-
});
|
|
8727
|
-
},
|
|
8728
|
-
/**
|
|
8729
|
-
* Round the given coordinates to a given snap value
|
|
8730
|
-
* (e.g., nearest 0.2 increment)
|
|
8731
|
-
*/
|
|
8732
|
-
snapCoord: function (coord, snap) {
|
|
8733
|
-
return _.map(coord, function (val, i) {
|
|
8734
|
-
return KhanMath.roundToNearest(snap[i], val);
|
|
8735
|
-
});
|
|
8736
|
-
},
|
|
8737
|
-
// Find the angle in degrees between two or three points
|
|
8738
|
-
findAngle: function (point1, point2, vertex) {
|
|
8739
|
-
if (vertex === undefined) {
|
|
8740
|
-
const x = point1[0] - point2[0];
|
|
8741
|
-
const y = point1[1] - point2[1];
|
|
8742
|
-
if (!x && !y) {
|
|
8743
|
-
return 0;
|
|
8744
|
-
}
|
|
8745
|
-
return (180 + Math.atan2(-y, -x) * 180 / Math.PI + 360) % 360;
|
|
8746
|
-
}
|
|
8747
|
-
return GraphUtils.findAngle(point1, vertex) - GraphUtils.findAngle(point2, vertex);
|
|
8748
|
-
},
|
|
8749
|
-
graphs: {}
|
|
8750
|
-
};
|
|
8751
8688
|
var _bounds = /*#__PURE__*/_classPrivateFieldKey("bounds");
|
|
8752
8689
|
var _drawingTransform = /*#__PURE__*/_classPrivateFieldKey("drawingTransform");
|
|
8753
8690
|
class Graphie$1 {
|
|
@@ -8776,8 +8713,59 @@ class Graphie$1 {
|
|
|
8776
8713
|
// Refactor to accessor methods that look at this.dimensions.
|
|
8777
8714
|
this.xpixels = void 0;
|
|
8778
8715
|
this.ypixels = void 0;
|
|
8716
|
+
this.label = (point, text, direction, arg4, arg5) => {
|
|
8717
|
+
const style = typeof arg4 === "object" ? arg4 : arg5;
|
|
8718
|
+
const latex = typeof arg4 === "boolean" ? arg4 : true;
|
|
8719
|
+
return this.withStyle(style, () => {
|
|
8720
|
+
const $span = $("<span>").addClass("graphie-label");
|
|
8721
|
+
const pad = this.currentStyle["label-distance"];
|
|
8722
|
+
$span.css({
|
|
8723
|
+
position: "absolute",
|
|
8724
|
+
padding: (pad != null ? pad : 7) + "px",
|
|
8725
|
+
color: "black"
|
|
8726
|
+
}).data("labelDirection", direction).appendTo(this.el);
|
|
8727
|
+
|
|
8728
|
+
// @ts-expect-error - TS2339 - Property 'setPosition' does not exist on type 'JQuery<HTMLElement>'.
|
|
8729
|
+
$span.setPosition = point => {
|
|
8730
|
+
const scaledPoint = this.scalePoint(point);
|
|
8731
|
+
$span.css({
|
|
8732
|
+
left: scaledPoint[0],
|
|
8733
|
+
top: scaledPoint[1]
|
|
8734
|
+
});
|
|
8735
|
+
};
|
|
8736
|
+
|
|
8737
|
+
// @ts-expect-error - TS2339 - Property 'setPosition' does not exist on type 'JQuery<HTMLElement>'.
|
|
8738
|
+
$span.setPosition(point);
|
|
8739
|
+
const span = $span[0];
|
|
8740
|
+
|
|
8741
|
+
// @ts-expect-error - TS2339 - Property 'processMath' does not exist on type 'JQuery<HTMLElement>'.
|
|
8742
|
+
$span.processMath = function (math, force) {
|
|
8743
|
+
processMath$1(span, math, force, function () {
|
|
8744
|
+
const width = span.scrollWidth;
|
|
8745
|
+
const height = span.scrollHeight;
|
|
8746
|
+
setLabelMargins(span, [width, height]);
|
|
8747
|
+
});
|
|
8748
|
+
};
|
|
8749
|
+
|
|
8750
|
+
// @ts-expect-error - TS2339 - Property 'processText' does not exist on type 'JQuery<HTMLElement>'.
|
|
8751
|
+
$span.processText = function (text) {
|
|
8752
|
+
$span.html(text);
|
|
8753
|
+
const width = span.scrollWidth;
|
|
8754
|
+
const height = span.scrollHeight;
|
|
8755
|
+
setLabelMargins(span, [width, height]);
|
|
8756
|
+
};
|
|
8757
|
+
if (latex) {
|
|
8758
|
+
// @ts-expect-error - TS2339 - Property 'processMath' does not exist on type 'JQuery<HTMLElement>'.
|
|
8759
|
+
$span.processMath(text, /* force */false);
|
|
8760
|
+
} else {
|
|
8761
|
+
// @ts-expect-error - TS2339 - Property 'processText' does not exist on type 'JQuery<HTMLElement>'.
|
|
8762
|
+
$span.processText(text);
|
|
8763
|
+
}
|
|
8764
|
+
return $span;
|
|
8765
|
+
});
|
|
8766
|
+
};
|
|
8779
8767
|
this.svgPath = (points, alreadyScaled) => {
|
|
8780
|
-
return
|
|
8768
|
+
return points.map((point, i) => {
|
|
8781
8769
|
if (point === true) {
|
|
8782
8770
|
return "z";
|
|
8783
8771
|
}
|
|
@@ -8793,7 +8781,6 @@ class Graphie$1 {
|
|
|
8793
8781
|
// If points are collinear, plot a line instead
|
|
8794
8782
|
if (a === 0) {
|
|
8795
8783
|
const _points = [[this.bounds().xMin, computeParabola(this.bounds().xMin)], [this.bounds().xMax, computeParabola(this.bounds().xMax)]];
|
|
8796
|
-
// @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
|
|
8797
8784
|
return this.svgPath(_points);
|
|
8798
8785
|
}
|
|
8799
8786
|
|
|
@@ -8818,9 +8805,8 @@ class Graphie$1 {
|
|
|
8818
8805
|
const right = [vertex[0] + dx, point[1]];
|
|
8819
8806
|
|
|
8820
8807
|
// Scale and bound
|
|
8821
|
-
|
|
8822
|
-
const
|
|
8823
|
-
const values = _.map(_.flatten(points), KhanMath.bound);
|
|
8808
|
+
const points = [left, control, right].map(this.scalePoint);
|
|
8809
|
+
const values = points.flat().map(KhanMath.bound);
|
|
8824
8810
|
return "M" + values[0] + "," + values[1] + " Q" + values[2] + "," + values[3] + " " + values[4] + "," + values[5];
|
|
8825
8811
|
};
|
|
8826
8812
|
this.svgSinusoidPath = (a, b, c, d) => {
|
|
@@ -8842,10 +8828,8 @@ class Graphie$1 {
|
|
|
8842
8828
|
// See: http://stackoverflow.com/questions/13932704/how-to-draw-sine-waves-with-svg-js
|
|
8843
8829
|
const xCoords = [x0, x0 * 2 / 3 + x1 * 1 / 3, x0 * 1 / 3 + x1 * 2 / 3, x1];
|
|
8844
8830
|
const yCoords = [computeSine(x0), computeSine(x0) + computeDerivative(x0) * (x1 - x0) / 3, computeSine(x1) - computeDerivative(x1) * (x1 - x0) / 3, computeSine(x1)];
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
// @ts-expect-error - TS2345 - Argument of type '(point: number | Coord) => Coord' is not assignable to parameter of type 'Iteratee<any[][], any, any[]>'.
|
|
8848
|
-
return _.map(_.zip(xCoords, yCoords), this.scalePoint);
|
|
8831
|
+
const points = vector$1.zip(xCoords, yCoords);
|
|
8832
|
+
return points.map(this.scalePoint);
|
|
8849
8833
|
};
|
|
8850
8834
|
|
|
8851
8835
|
// How many quarter-periods do we need to span the graph?
|
|
@@ -8886,6 +8870,7 @@ class Graphie$1 {
|
|
|
8886
8870
|
$(el).children("div").css("position", "absolute");
|
|
8887
8871
|
}
|
|
8888
8872
|
init(options) {
|
|
8873
|
+
var _options$isMobile;
|
|
8889
8874
|
let scale = options.scale || [40, 40];
|
|
8890
8875
|
scale = typeof scale === "number" ? [scale, scale] : scale;
|
|
8891
8876
|
if (options.range == null) {
|
|
@@ -8905,7 +8890,7 @@ class Graphie$1 {
|
|
|
8905
8890
|
this.dimensions = [w, h];
|
|
8906
8891
|
this.xpixels = w;
|
|
8907
8892
|
this.ypixels = h;
|
|
8908
|
-
this.isMobile = options.isMobile;
|
|
8893
|
+
this.isMobile = (_options$isMobile = options.isMobile) != null ? _options$isMobile : false;
|
|
8909
8894
|
return this;
|
|
8910
8895
|
}
|
|
8911
8896
|
|
|
@@ -8923,33 +8908,24 @@ class Graphie$1 {
|
|
|
8923
8908
|
// - xLabelFormat: fn to format label string for x-axis
|
|
8924
8909
|
graphInit(options) {
|
|
8925
8910
|
options = options || {};
|
|
8926
|
-
|
|
8911
|
+
for (const [prop, val] of entries(options)) {
|
|
8927
8912
|
// allow options to be specified by a single number for shorthand if
|
|
8928
8913
|
// the horizontal and vertical components are the same
|
|
8929
|
-
if (
|
|
8930
|
-
// @ts-expect-error - TS2339 - Property 'match' does not exist on type 'string | number | symbol'.
|
|
8931
|
-
!prop.match(/.*Opacity$/) && prop !== "range" && typeof val === "number") {
|
|
8914
|
+
if (!prop.match(/.*Opacity$/) && prop !== "range" && typeof val === "number") {
|
|
8932
8915
|
options[prop] = [val, val];
|
|
8933
8916
|
}
|
|
8934
8917
|
|
|
8935
8918
|
// allow symmetric ranges to be specified by the absolute values
|
|
8936
8919
|
if (prop === "range" || prop === "gridRange") {
|
|
8937
|
-
|
|
8938
|
-
// but don't mandate symmetric ranges
|
|
8939
|
-
if (val[0].constructor !== Array) {
|
|
8940
|
-
options[prop] = [[-val[0], val[0]], [-val[1], val[1]]];
|
|
8941
|
-
}
|
|
8942
|
-
} else if (typeof val === "number") {
|
|
8943
|
-
options[prop] = [[-val, val], [-val, val]];
|
|
8944
|
-
}
|
|
8920
|
+
options[prop] = normalizeRange(options[prop]);
|
|
8945
8921
|
}
|
|
8946
|
-
}
|
|
8947
|
-
const range = options.range || [[-10, 10], [-10, 10]];
|
|
8948
|
-
const gridRange = options.gridRange ||
|
|
8922
|
+
}
|
|
8923
|
+
const range = normalizeRange(options.range || [[-10, 10], [-10, 10]]);
|
|
8924
|
+
const gridRange = normalizeRange(options.gridRange || range);
|
|
8949
8925
|
const scale = options.scale || [20, 20];
|
|
8950
8926
|
const grid = options.grid != null ? options.grid : true;
|
|
8951
8927
|
const gridOpacity = options.gridOpacity || 0.1;
|
|
8952
|
-
const gridStep = options.gridStep || [1, 1];
|
|
8928
|
+
const gridStep = toPair(options.gridStep || [1, 1]);
|
|
8953
8929
|
const axes = options.axes != null ? options.axes : true;
|
|
8954
8930
|
const axisArrows = options.axisArrows || "";
|
|
8955
8931
|
const axisOpacity = options.axisOpacity || 1.0;
|
|
@@ -8969,7 +8945,7 @@ class Graphie$1 {
|
|
|
8969
8945
|
let xLabelFormat = options.xLabelFormat || labelFormat;
|
|
8970
8946
|
let yLabelFormat = options.yLabelFormat || labelFormat;
|
|
8971
8947
|
const realRange = [[range[0][0] - (range[0][0] > 0 ? 1 : 0), range[0][1] + (range[0][1] < 0 ? 1 : 0)], [range[1][0] - (range[1][0] > 0 ? 1 : 0), range[1][1] + (range[1][1] < 0 ? 1 : 0)]];
|
|
8972
|
-
if (!
|
|
8948
|
+
if (!Array.isArray(unityLabels)) {
|
|
8973
8949
|
unityLabels = [unityLabels, unityLabels];
|
|
8974
8950
|
}
|
|
8975
8951
|
const minusIgnorer = function minusIgnorer(lf) {
|
|
@@ -9044,7 +9020,7 @@ class Graphie$1 {
|
|
|
9044
9020
|
stroke: options.isMobile ? KhanColors.GRAY_G : "#000000",
|
|
9045
9021
|
opacity: options.isMobile ? 1 : tickOpacity,
|
|
9046
9022
|
strokeWidth: 1
|
|
9047
|
-
},
|
|
9023
|
+
}, () => {
|
|
9048
9024
|
// horizontal axis
|
|
9049
9025
|
let step = gridStep[0] * tickStep[0];
|
|
9050
9026
|
let len = tickLen[0] / scale[1];
|
|
@@ -9071,13 +9047,11 @@ class Graphie$1 {
|
|
|
9071
9047
|
if (range[0][0] < 0 && range[0][1] > 0) {
|
|
9072
9048
|
for (let y = step + axisCenter[1]; y <= stop; y += step) {
|
|
9073
9049
|
if (y < stop || !axisArrows) {
|
|
9074
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
9075
9050
|
this.line([-len + axisCenter[0], y], [halfWidthTicks ? 0 : len + axisCenter[0], y]);
|
|
9076
9051
|
}
|
|
9077
9052
|
}
|
|
9078
9053
|
for (let y = -step + axisCenter[1]; y >= start; y -= step) {
|
|
9079
9054
|
if (y > start || !axisArrows) {
|
|
9080
|
-
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
9081
9055
|
this.line([-len + axisCenter[0], y], [halfWidthTicks ? 0 : len + axisCenter[0], y]);
|
|
9082
9056
|
}
|
|
9083
9057
|
}
|
|
@@ -9153,155 +9127,346 @@ class Graphie$1 {
|
|
|
9153
9127
|
const processed = this.processAttributes(attrs);
|
|
9154
9128
|
if (typeof fn === "function") {
|
|
9155
9129
|
const oldStyle = this.currentStyle;
|
|
9156
|
-
this.currentStyle =
|
|
9130
|
+
this.currentStyle = _extends({}, this.currentStyle, processed);
|
|
9157
9131
|
const result = fn.call(this);
|
|
9158
9132
|
this.currentStyle = oldStyle;
|
|
9159
9133
|
return result;
|
|
9160
9134
|
}
|
|
9161
|
-
|
|
9135
|
+
Object.assign(this.currentStyle, processed);
|
|
9136
|
+
}
|
|
9137
|
+
grid(xr, yr, style) {
|
|
9138
|
+
return this.withStyle(style, () => {
|
|
9139
|
+
const step = this.currentStyle.step || [1, 1];
|
|
9140
|
+
const set = this.raphael.set();
|
|
9141
|
+
let x = step[0] * Math.ceil(xr[0] / step[0]);
|
|
9142
|
+
for (; x <= xr[1]; x += step[0]) {
|
|
9143
|
+
set.push(this.line([x, yr[0]], [x, yr[1]]));
|
|
9144
|
+
}
|
|
9145
|
+
let y = step[1] * Math.ceil(yr[0] / step[1]);
|
|
9146
|
+
for (; y <= yr[1]; y += step[1]) {
|
|
9147
|
+
set.push(this.line([xr[0], y], [xr[1], y]));
|
|
9148
|
+
}
|
|
9149
|
+
return set;
|
|
9150
|
+
});
|
|
9151
|
+
}
|
|
9152
|
+
arc(center, radius, startAngle, endAngle, sector, style) {
|
|
9153
|
+
return this.withStyle(style, () => {
|
|
9154
|
+
startAngle = (startAngle % 360 + 360) % 360;
|
|
9155
|
+
endAngle = (endAngle % 360 + 360) % 360;
|
|
9156
|
+
const cent = this.scalePoint(center);
|
|
9157
|
+
const radii = this.scaleVector(radius);
|
|
9158
|
+
const startVector = polar(radius, startAngle);
|
|
9159
|
+
const endVector = polar(radius, endAngle);
|
|
9160
|
+
|
|
9161
|
+
// We round the coordinates to make testing easier, because trig
|
|
9162
|
+
// operations return unround numbers.
|
|
9163
|
+
// TODO(benchristel): move rounding to scalePoint?
|
|
9164
|
+
const round = x => number.round(x, 6);
|
|
9165
|
+
const startPoint = this.scalePoint([round(center[0] + startVector[0]), round(center[1] + startVector[1])]);
|
|
9166
|
+
const endPoint = this.scalePoint([round(center[0] + endVector[0]), round(center[1] + endVector[1])]);
|
|
9167
|
+
const largeAngle = ((endAngle - startAngle) % 360 + 360) % 360 > 180;
|
|
9168
|
+
return this.raphael.path("M" + startPoint.join(" ") + "A" + radii.join(" ") + " 0 " + (
|
|
9169
|
+
// ellipse rotation
|
|
9170
|
+
largeAngle ? 1 : 0) + " 0 " +
|
|
9171
|
+
// sweep flag
|
|
9172
|
+
endPoint.join(" ") + (sector ? "L" + cent.join(" ") + "z" : ""));
|
|
9173
|
+
});
|
|
9174
|
+
}
|
|
9175
|
+
circle(center, radius, style) {
|
|
9176
|
+
return this.withStyle(style, () => this.raphael.ellipse(...this.scalePoint(center), ...this.scaleVector([radius, radius])));
|
|
9162
9177
|
}
|
|
9163
|
-
label(point, text, direction, latex) {}
|
|
9164
|
-
grid(xr, yr, styleAttributes) {}
|
|
9165
9178
|
|
|
9166
|
-
//
|
|
9167
|
-
|
|
9168
|
-
|
|
9179
|
+
// (x, y) is coordinate of bottom left corner
|
|
9180
|
+
rect(x, y, width, height, style) {
|
|
9181
|
+
return this.withStyle(style, () => {
|
|
9182
|
+
// Raphael needs (x, y) to be coordinate of upper left corner
|
|
9183
|
+
const corner = this.scalePoint([x, y + height]);
|
|
9184
|
+
const dims = this.scaleVector([width, height]);
|
|
9185
|
+
const elem = this.raphael.rect(...corner.concat(dims));
|
|
9186
|
+
if (this.isMobile) {
|
|
9187
|
+
elem.node.style.shapeRendering = "crispEdges";
|
|
9188
|
+
}
|
|
9189
|
+
return elem;
|
|
9190
|
+
});
|
|
9191
|
+
}
|
|
9192
|
+
ellipse(center, radii, style) {
|
|
9193
|
+
return this.withStyle(style, () => this.raphael.ellipse(...this.scalePoint(center).concat(this.scaleVector(radii))));
|
|
9194
|
+
}
|
|
9195
|
+
fixedEllipse(center,
|
|
9196
|
+
// Different type than Coord, this is radiusX, radiusY
|
|
9197
|
+
radii, maxScale, padding, style) {
|
|
9198
|
+
return this.withStyle(style, () => {
|
|
9199
|
+
// Scale point and radius
|
|
9200
|
+
const scaledPoint = this.scalePoint(center);
|
|
9201
|
+
const scaledRadii = this.scaleVector(radii);
|
|
9202
|
+
const width = 2 * scaledRadii[0] * maxScale + padding;
|
|
9203
|
+
const height = 2 * scaledRadii[1] * maxScale + padding;
|
|
9204
|
+
|
|
9205
|
+
// Calculate absolute left, top
|
|
9206
|
+
const left = scaledPoint[0] - width / 2;
|
|
9207
|
+
const top = scaledPoint[1] - height / 2;
|
|
9208
|
+
|
|
9209
|
+
// Wrap in <div>
|
|
9210
|
+
const wrapper = document.createElement("div");
|
|
9211
|
+
$(wrapper).css({
|
|
9212
|
+
position: "absolute",
|
|
9213
|
+
width: width + "px",
|
|
9214
|
+
height: height + "px",
|
|
9215
|
+
left: left + "px",
|
|
9216
|
+
top: top + "px"
|
|
9217
|
+
});
|
|
9218
|
+
// wrapper.setAttribute("data-graphie-type", "ellipse");
|
|
9169
9219
|
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9220
|
+
// Create Raphael canvas
|
|
9221
|
+
const localRaphael = raphael.exports(wrapper, width, height);
|
|
9222
|
+
const visibleShape = localRaphael.ellipse(width / 2, height / 2, scaledRadii[0], scaledRadii[1]);
|
|
9223
|
+
return {
|
|
9224
|
+
wrapper: wrapper,
|
|
9225
|
+
visibleShape: visibleShape
|
|
9226
|
+
};
|
|
9227
|
+
});
|
|
9228
|
+
}
|
|
9229
|
+
unstyledPath(points) {
|
|
9230
|
+
const p = this.raphael.path(this.svgPath(points));
|
|
9231
|
+
p.graphiePath = points;
|
|
9232
|
+
return p;
|
|
9233
|
+
}
|
|
9234
|
+
path(points, style) {
|
|
9235
|
+
return this.withStyle(style, () => {
|
|
9236
|
+
return this.unstyledPath(points);
|
|
9237
|
+
});
|
|
9238
|
+
}
|
|
9182
9239
|
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9240
|
+
// fixedPath is a stub that gets overwritten with a function from
|
|
9241
|
+
// drawingTools in createGraphie
|
|
9242
|
+
fixedPath(points, center, createPath) {
|
|
9243
|
+
points = points.map(this.scalePoint);
|
|
9244
|
+
center = center ? this.scalePoint(center) : null;
|
|
9245
|
+
createPath = createPath || this.svgPath;
|
|
9246
|
+
|
|
9247
|
+
// Compute bounding box
|
|
9248
|
+
const xs = points.map(p => p[0]);
|
|
9249
|
+
const ys = points.map(p => p[1]);
|
|
9250
|
+
const pathLeft = Math.min(...xs);
|
|
9251
|
+
const pathRight = Math.max(...xs);
|
|
9252
|
+
const pathTop = Math.min(...ys);
|
|
9253
|
+
const pathBottom = Math.max(...ys);
|
|
9254
|
+
|
|
9255
|
+
// Apply padding to line
|
|
9256
|
+
const padding = [4, 4];
|
|
9257
|
+
|
|
9258
|
+
// Calculate and apply additional offset
|
|
9259
|
+
const topLeftOfBoundingBox = [pathLeft, pathTop];
|
|
9260
|
+
|
|
9261
|
+
// Apply padding and offset to points to convert from
|
|
9262
|
+
// canvas coordinates to pixel coordinates relative to bounding box
|
|
9263
|
+
points = points.map(function (point) {
|
|
9264
|
+
return vector$1.add(vector$1.subtract(point, topLeftOfBoundingBox), vector$1.scale(padding, 0.5));
|
|
9265
|
+
});
|
|
9266
|
+
|
|
9267
|
+
// Calculate <div> dimensions
|
|
9268
|
+
const width = pathRight - pathLeft + padding[0];
|
|
9269
|
+
const height = pathBottom - pathTop + padding[1];
|
|
9270
|
+
const left = topLeftOfBoundingBox[0] - padding[0] / 2;
|
|
9271
|
+
const top = topLeftOfBoundingBox[1] - padding[1] / 2;
|
|
9272
|
+
|
|
9273
|
+
// Create <div>
|
|
9274
|
+
const wrapper = document.createElement("div");
|
|
9275
|
+
$(wrapper).css({
|
|
9276
|
+
position: "absolute",
|
|
9277
|
+
width: width + "px",
|
|
9278
|
+
height: height + "px",
|
|
9279
|
+
left: left + "px",
|
|
9280
|
+
top: top + "px",
|
|
9281
|
+
// If user specified a center, set it
|
|
9282
|
+
// NOTE(kevinb): jQuery doesn't like that `transformOrigin `could be `null`
|
|
9283
|
+
// so we cast to `any` here.
|
|
9284
|
+
transformOrigin: center ? width / 2 + center[0] + "px " + (height / 2 + center[1]) + "px" : null
|
|
9285
|
+
});
|
|
9286
|
+
|
|
9287
|
+
// Create Raphael canvas
|
|
9288
|
+
const localRaphael = raphael.exports(wrapper, width, height);
|
|
9289
|
+
|
|
9290
|
+
// Calculate path
|
|
9291
|
+
const visibleShape = localRaphael.path(createPath(points));
|
|
9292
|
+
return {
|
|
9293
|
+
wrapper: wrapper,
|
|
9294
|
+
visibleShape: visibleShape
|
|
9209
9295
|
};
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9296
|
+
}
|
|
9297
|
+
scaledPath(points, style) {
|
|
9298
|
+
return this.withStyle(style, () => {
|
|
9299
|
+
const p = this.raphael.path(this.svgPath(points, /* alreadyScaled */true));
|
|
9300
|
+
p.graphiePath = points;
|
|
9301
|
+
return p;
|
|
9302
|
+
});
|
|
9303
|
+
}
|
|
9304
|
+
line(start, end, style) {
|
|
9305
|
+
return this.withStyle(style, () => {
|
|
9306
|
+
const l = this.unstyledPath([start, end]);
|
|
9307
|
+
if (this.isMobile) {
|
|
9308
|
+
l.node.style.shapeRendering = "crispEdges";
|
|
9220
9309
|
}
|
|
9310
|
+
return l;
|
|
9221
9311
|
});
|
|
9222
|
-
return processed;
|
|
9223
9312
|
}
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
right: [0.0, -0.5],
|
|
9231
|
-
"below right": [0.0, 0.0],
|
|
9232
|
-
below: [-0.5, 0.0],
|
|
9233
|
-
"below left": [-1.0, 0.0],
|
|
9234
|
-
left: [-1.0, -0.5],
|
|
9235
|
-
"above left": [-1.0, -1.0]
|
|
9236
|
-
};
|
|
9313
|
+
parabola(a, b, c, style) {
|
|
9314
|
+
return this.withStyle(style, () => this.raphael.path(this.svgParabolaPath(a, b, c)));
|
|
9315
|
+
}
|
|
9316
|
+
fixedLine(start, end, thickness) {
|
|
9317
|
+
// Apply padding to line
|
|
9318
|
+
const padding = [thickness, thickness];
|
|
9237
9319
|
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
"stroke-width": null
|
|
9249
|
-
};
|
|
9250
|
-
GraphUtils.createGraphie = function (el) {
|
|
9251
|
-
const thisGraphie = new Graphie$1(el);
|
|
9252
|
-
const setLabelMargins = function setLabelMargins(span, size) {
|
|
9253
|
-
const $span = $(span);
|
|
9254
|
-
const direction = $span.data("labelDirection");
|
|
9255
|
-
let [width, height] = size;
|
|
9256
|
-
// This can happen when a span
|
|
9257
|
-
// is invisible but we still want to update the CSS. At worst, we will
|
|
9258
|
-
// be off by a few pixels instead of in a different position entirely.
|
|
9259
|
-
if (width === 0 && height === 0) {
|
|
9260
|
-
[width, height] = [1, 1];
|
|
9261
|
-
Log.log("Label size was 0x0 in graphie.js; using 1x1 instead");
|
|
9262
|
-
}
|
|
9263
|
-
$span.css("visibility", "");
|
|
9264
|
-
if (typeof direction === "number") {
|
|
9265
|
-
const x = Math.cos(direction);
|
|
9266
|
-
const y = Math.sin(direction);
|
|
9267
|
-
const scale = Math.min(width / 2 / Math.abs(x), height / 2 / Math.abs(y));
|
|
9268
|
-
$span.css({
|
|
9269
|
-
marginLeft: -width / 2 + x * scale,
|
|
9270
|
-
marginTop: -height / 2 - y * scale
|
|
9271
|
-
});
|
|
9272
|
-
} else {
|
|
9273
|
-
const multipliers = labelDirections[direction || "center"];
|
|
9274
|
-
$span.css({
|
|
9275
|
-
marginLeft: Math.round(width * multipliers[0]),
|
|
9276
|
-
marginTop: Math.round(height * multipliers[1])
|
|
9277
|
-
});
|
|
9278
|
-
}
|
|
9279
|
-
};
|
|
9320
|
+
// Scale points to get values in pixels
|
|
9321
|
+
start = this.scalePoint(start);
|
|
9322
|
+
end = this.scalePoint(end);
|
|
9323
|
+
|
|
9324
|
+
// Calculate and apply additional offset
|
|
9325
|
+
const extraOffset = [Math.min(start[0], end[0]), Math.min(start[1], end[1])];
|
|
9326
|
+
|
|
9327
|
+
// Apply padding and offset to start, end points
|
|
9328
|
+
start = vector$1.add(vector$1.subtract(start, extraOffset), vector$1.scale(padding, 0.5));
|
|
9329
|
+
end = vector$1.add(vector$1.subtract(end, extraOffset), vector$1.scale(padding, 0.5));
|
|
9280
9330
|
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9331
|
+
// Calculate <div> dimensions
|
|
9332
|
+
const left = extraOffset[0] - padding[0] / 2;
|
|
9333
|
+
const top = extraOffset[1] - padding[1] / 2;
|
|
9334
|
+
const width = Math.abs(start[0] - end[0]) + padding[0];
|
|
9335
|
+
const height = Math.abs(start[1] - end[1]) + padding[1];
|
|
9336
|
+
|
|
9337
|
+
// Create <div>
|
|
9338
|
+
const wrapper = document.createElement("div");
|
|
9339
|
+
$(wrapper).css({
|
|
9340
|
+
position: "absolute",
|
|
9341
|
+
width: width + "px",
|
|
9342
|
+
height: height + "px",
|
|
9343
|
+
left: left + "px",
|
|
9344
|
+
top: top + "px",
|
|
9345
|
+
// Outsiders should feel like the line's 'origin' (i.e., for
|
|
9346
|
+
// rotation) is the starting point
|
|
9347
|
+
transformOrigin: start[0] + "px " + start[1] + "px"
|
|
9348
|
+
});
|
|
9349
|
+
|
|
9350
|
+
// Create Raphael canvas
|
|
9351
|
+
const localRaphael = raphael.exports(wrapper, width, height);
|
|
9352
|
+
|
|
9353
|
+
// Calculate path
|
|
9354
|
+
const path = "M" + start[0] + " " + start[1] + " " + "L" + end[0] + " " + end[1];
|
|
9355
|
+
const visibleShape = localRaphael.path(path);
|
|
9356
|
+
visibleShape.graphiePath = [start, end];
|
|
9357
|
+
return {
|
|
9358
|
+
wrapper: wrapper,
|
|
9359
|
+
visibleShape: visibleShape
|
|
9360
|
+
};
|
|
9361
|
+
}
|
|
9362
|
+
sinusoid(a, b, c, d, style) {
|
|
9363
|
+
return this.withStyle(style, () =>
|
|
9364
|
+
// Plot a sinusoid of the form: f(x) = a * sin(b * x - c) + d
|
|
9365
|
+
this.raphael.path(this.svgSinusoidPath(a, b, c, d)));
|
|
9366
|
+
}
|
|
9367
|
+
plotParametric(fn, range, style) {
|
|
9368
|
+
return this.withStyle(style, () => {
|
|
9369
|
+
// We truncate to 500,000, since anything bigger causes
|
|
9370
|
+
// overflow in the firefox svg renderer. This is safe
|
|
9371
|
+
// since 500,000 is outside the viewport anyway. We
|
|
9372
|
+
// write these functions the way we do to handle undefined.
|
|
9373
|
+
const clip = xy => {
|
|
9374
|
+
if (Math.abs(xy[1]) > 500000) {
|
|
9375
|
+
return [xy[0], Math.min(Math.max(xy[1], -500000), 500000)];
|
|
9376
|
+
}
|
|
9377
|
+
return xy;
|
|
9378
|
+
};
|
|
9379
|
+
const clippedFn = x => clip(fn(x));
|
|
9380
|
+
const min = range[0];
|
|
9381
|
+
const max = range[1];
|
|
9382
|
+
let step = (max - min) / (this.currentStyle["plot-points"] || 800);
|
|
9383
|
+
if (step === 0) {
|
|
9384
|
+
step = 1;
|
|
9385
|
+
}
|
|
9386
|
+
const paths = this.raphael.set();
|
|
9387
|
+
let points = [];
|
|
9388
|
+
let lastY = clippedFn(min)[1];
|
|
9389
|
+
for (let t = min; t <= max; t += step) {
|
|
9390
|
+
const point = clippedFn(t);
|
|
9391
|
+
const y = point[1];
|
|
9392
|
+
|
|
9393
|
+
// Find points where it flips
|
|
9394
|
+
if (
|
|
9395
|
+
// if there is an asymptote here, meaning that the graph
|
|
9396
|
+
// switches signs and has a large difference
|
|
9397
|
+
y > 0 !== lastY > 0 && Math.abs(y - lastY) > 2 * this.drawingTransform().pixelsPerUnitY() ||
|
|
9398
|
+
// or the function is undefined
|
|
9399
|
+
isNaN(y)) {
|
|
9400
|
+
// split the path at this point, and draw it
|
|
9401
|
+
paths.push(this.unstyledPath(points));
|
|
9402
|
+
// restart the path, excluding this point
|
|
9403
|
+
points = [];
|
|
9404
|
+
} else {
|
|
9405
|
+
// otherwise, just add the point to the path
|
|
9406
|
+
points.push(point);
|
|
9407
|
+
}
|
|
9408
|
+
lastY = y;
|
|
9409
|
+
}
|
|
9410
|
+
paths.push(this.unstyledPath(points));
|
|
9411
|
+
return paths;
|
|
9412
|
+
});
|
|
9413
|
+
}
|
|
9414
|
+
plot(fn, range, style) {
|
|
9415
|
+
return this.withStyle(style, () => {
|
|
9416
|
+
const min = range[0];
|
|
9417
|
+
const max = range[1];
|
|
9418
|
+
if (!this.currentStyle["plot-points"]) {
|
|
9419
|
+
this.currentStyle["plot-points"] = 2 * (max - min) * this.drawingTransform().pixelsPerUnitX();
|
|
9420
|
+
}
|
|
9421
|
+
const parametricFn = x => [x, fn(x)];
|
|
9422
|
+
return this.plotParametric(parametricFn, range);
|
|
9423
|
+
});
|
|
9424
|
+
}
|
|
9425
|
+
withStyle(style, fn) {
|
|
9426
|
+
const oldStyle = this.currentStyle;
|
|
9427
|
+
this.currentStyle = _extends({}, this.currentStyle, this.processAttributes(style));
|
|
9428
|
+
const result = this.postprocessDrawingResult(fn());
|
|
9429
|
+
this.currentStyle = oldStyle;
|
|
9430
|
+
return result;
|
|
9431
|
+
}
|
|
9432
|
+
postprocessDrawingResult(result) {
|
|
9433
|
+
// Bad heuristic for recognizing Raphael elements and sets
|
|
9434
|
+
const type = result.constructor.prototype;
|
|
9435
|
+
if (type === raphael.exports.el || type === raphael.exports.st) {
|
|
9436
|
+
result.attr(this.currentStyle);
|
|
9437
|
+
if (this.currentStyle.arrows) {
|
|
9438
|
+
result = this.addArrowheads(result);
|
|
9439
|
+
}
|
|
9440
|
+
} else if (result instanceof $) {
|
|
9441
|
+
// We assume that if it's not a Raphael element/set, it
|
|
9442
|
+
// does not contain SVG.
|
|
9443
|
+
// @ts-expect-error - TS2339 - Property 'css' does not exist on type '{}'.
|
|
9444
|
+
result.css(_extends({}, this.currentStyle, SVG_SPECIFIC_STYLE_MASK));
|
|
9445
|
+
}
|
|
9446
|
+
return result;
|
|
9447
|
+
}
|
|
9448
|
+
addArrowheads(path) {
|
|
9284
9449
|
const type = path.constructor.prototype;
|
|
9285
9450
|
if (type === raphael.exports.el) {
|
|
9286
9451
|
if (path.type === "path" && typeof path.arrowheadsDrawn === "undefined") {
|
|
9287
9452
|
const w = path.attr("stroke-width");
|
|
9288
9453
|
const s = 0.6 + 0.4 * w;
|
|
9289
9454
|
const l = path.getTotalLength();
|
|
9290
|
-
const set =
|
|
9291
|
-
const head =
|
|
9455
|
+
const set = this.raphael.set();
|
|
9456
|
+
const head = this.raphael.path(this.isMobile ? "M-4,4 C-4,4 -0.25,0 -0.25,0 C-0.25,0 -4,-4 -4,-4" : "M-3 4 C-2.75 2.5 0 0.25 0.75 0C0 -0.25 -2.75 -2.5 -3 -4");
|
|
9292
9457
|
const end = path.getPointAtLength(l - 0.4);
|
|
9293
9458
|
const almostTheEnd = path.getPointAtLength(l - 0.75 * s);
|
|
9294
9459
|
const angle = Math.atan2(end.y - almostTheEnd.y, end.x - almostTheEnd.x) * 180 / Math.PI;
|
|
9295
9460
|
const attrs = path.attr();
|
|
9296
9461
|
delete attrs.path;
|
|
9297
9462
|
let subpath = path.getSubpath(0, l - 0.75 * s);
|
|
9298
|
-
subpath =
|
|
9463
|
+
subpath = this.raphael.path(subpath).attr(attrs);
|
|
9299
9464
|
subpath.arrowheadsDrawn = true;
|
|
9300
9465
|
path.remove();
|
|
9301
9466
|
|
|
9302
9467
|
// For some unknown reason 0 doesn't work for the rotation
|
|
9303
9468
|
// origin so we use a tiny number.
|
|
9304
|
-
head.rotate(angle,
|
|
9469
|
+
head.rotate(angle, this.isMobile ? 1e-5 : 0.75, 0).scale(s, s, 0.75, 0).translate(almostTheEnd.x, almostTheEnd.y).attr(attrs).attr({
|
|
9305
9470
|
"stroke-linejoin": "round",
|
|
9306
9471
|
"stroke-linecap": "round"
|
|
9307
9472
|
});
|
|
@@ -9312,429 +9477,195 @@ GraphUtils.createGraphie = function (el) {
|
|
|
9312
9477
|
}
|
|
9313
9478
|
} else if (type === raphael.exports.st) {
|
|
9314
9479
|
for (let i = 0, l = path.items.length; i < l; i++) {
|
|
9315
|
-
|
|
9480
|
+
this.addArrowheads(path.items[i]);
|
|
9316
9481
|
}
|
|
9317
9482
|
}
|
|
9318
9483
|
return path;
|
|
9319
|
-
};
|
|
9320
|
-
function circle(center, radius) {
|
|
9321
|
-
return thisGraphie.raphael.ellipse(...thisGraphie.scalePoint(center).concat(thisGraphie.scaleVector([radius, radius])));
|
|
9322
|
-
}
|
|
9323
|
-
|
|
9324
|
-
// (x, y) is coordinate of bottom left corner
|
|
9325
|
-
function rect(x, y, width, height) {
|
|
9326
|
-
// Raphael needs (x, y) to be coordinate of upper left corner
|
|
9327
|
-
const corner = thisGraphie.scalePoint([x, y + height]);
|
|
9328
|
-
const dims = thisGraphie.scaleVector([width, height]);
|
|
9329
|
-
const elem = thisGraphie.raphael.rect(...corner.concat(dims));
|
|
9330
|
-
if (thisGraphie.isMobile) {
|
|
9331
|
-
elem.node.style.shapeRendering = "crispEdges";
|
|
9332
|
-
}
|
|
9333
|
-
return elem;
|
|
9334
|
-
}
|
|
9335
|
-
function ellipse(center, radii) {
|
|
9336
|
-
return thisGraphie.raphael.ellipse(...thisGraphie.scalePoint(center).concat(thisGraphie.scaleVector(radii)));
|
|
9337
9484
|
}
|
|
9338
|
-
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
// Calculate absolute left, top
|
|
9348
|
-
const left = scaledPoint[0] - width / 2;
|
|
9349
|
-
const top = scaledPoint[1] - height / 2;
|
|
9350
|
-
|
|
9351
|
-
// Wrap in <div>
|
|
9352
|
-
const wrapper = document.createElement("div");
|
|
9353
|
-
$(wrapper).css({
|
|
9354
|
-
position: "absolute",
|
|
9355
|
-
width: width + "px",
|
|
9356
|
-
height: height + "px",
|
|
9357
|
-
left: left + "px",
|
|
9358
|
-
top: top + "px"
|
|
9359
|
-
});
|
|
9360
|
-
// wrapper.setAttribute("data-graphie-type", "ellipse");
|
|
9485
|
+
processAttributes(attrs) {
|
|
9486
|
+
const transformers = {
|
|
9487
|
+
scale: scale => {
|
|
9488
|
+
this.drawingTransform().setScale(scale);
|
|
9489
|
+
},
|
|
9490
|
+
clipRect: pair => {
|
|
9491
|
+
const point = pair[0];
|
|
9492
|
+
const size = pair[1];
|
|
9493
|
+
point[1] += size[1]; // because our coordinates are flipped
|
|
9361
9494
|
|
|
9362
|
-
|
|
9363
|
-
|
|
9364
|
-
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9495
|
+
return {
|
|
9496
|
+
"clip-rect": this.scalePoint(point).concat(this.scaleVector(size)).join(" ")
|
|
9497
|
+
};
|
|
9498
|
+
},
|
|
9499
|
+
strokeWidth: function (val) {
|
|
9500
|
+
return {
|
|
9501
|
+
"stroke-width": parseFloat(val)
|
|
9502
|
+
};
|
|
9503
|
+
},
|
|
9504
|
+
rx: val => {
|
|
9505
|
+
return {
|
|
9506
|
+
rx: this.scaleVector([val, 0])[0]
|
|
9507
|
+
};
|
|
9508
|
+
},
|
|
9509
|
+
ry: val => {
|
|
9510
|
+
return {
|
|
9511
|
+
ry: this.scaleVector([0, val])[1]
|
|
9512
|
+
};
|
|
9513
|
+
},
|
|
9514
|
+
r: val => {
|
|
9515
|
+
const scaled = this.scaleVector([val, val]);
|
|
9516
|
+
return {
|
|
9517
|
+
rx: scaled[0],
|
|
9518
|
+
ry: scaled[1]
|
|
9519
|
+
};
|
|
9520
|
+
}
|
|
9368
9521
|
};
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
const endPoint = thisGraphie.scalePoint([center[0] + endVector[0], center[1] + endVector[1]]);
|
|
9379
|
-
const largeAngle = ((endAngle - startAngle) % 360 + 360) % 360 > 180;
|
|
9380
|
-
return thisGraphie.raphael.path("M" + startPoint.join(" ") + "A" + radii.join(" ") + " 0 " + (
|
|
9381
|
-
// ellipse rotation
|
|
9382
|
-
largeAngle ? 1 : 0) + " 0 " +
|
|
9383
|
-
// sweep flag
|
|
9384
|
-
endPoint.join(" ") + (sector ? "L" + cent.join(" ") + "z" : ""));
|
|
9385
|
-
}
|
|
9386
|
-
function path(points) {
|
|
9387
|
-
// @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
|
|
9388
|
-
const p = thisGraphie.raphael.path(thisGraphie.svgPath(points));
|
|
9389
|
-
p.graphiePath = points;
|
|
9390
|
-
return p;
|
|
9391
|
-
}
|
|
9392
|
-
function fixedPath(points, center, createPath) {
|
|
9393
|
-
points = _.map(points, thisGraphie.scalePoint);
|
|
9394
|
-
center = center ? thisGraphie.scalePoint(center) : null;
|
|
9395
|
-
createPath = createPath || thisGraphie.svgPath;
|
|
9396
|
-
const pathLeft = _.min(_.pluck(points, 0));
|
|
9397
|
-
const pathRight = _.max(_.pluck(points, 0));
|
|
9398
|
-
const pathTop = _.min(_.pluck(points, 1));
|
|
9399
|
-
const pathBottom = _.max(_.pluck(points, 1));
|
|
9400
|
-
|
|
9401
|
-
// Apply padding to line
|
|
9402
|
-
const padding = [4, 4];
|
|
9403
|
-
|
|
9404
|
-
// Calculate and apply additional offset
|
|
9405
|
-
const extraOffset = [pathLeft, pathTop];
|
|
9406
|
-
|
|
9407
|
-
// Apply padding and offset to points
|
|
9408
|
-
points = _.map(points, function (point) {
|
|
9409
|
-
return vector$1.add(vector$1.subtract(point, extraOffset), vector$1.scale(padding, 0.5));
|
|
9410
|
-
});
|
|
9411
|
-
|
|
9412
|
-
// Calculate <div> dimensions
|
|
9413
|
-
const width = pathRight - pathLeft + padding[0];
|
|
9414
|
-
const height = pathBottom - pathTop + padding[1];
|
|
9415
|
-
const left = extraOffset[0] - padding[0] / 2;
|
|
9416
|
-
const top = extraOffset[1] - padding[1] / 2;
|
|
9417
|
-
|
|
9418
|
-
// Create <div>
|
|
9419
|
-
const wrapper = document.createElement("div");
|
|
9420
|
-
$(wrapper).css({
|
|
9421
|
-
position: "absolute",
|
|
9422
|
-
width: width + "px",
|
|
9423
|
-
height: height + "px",
|
|
9424
|
-
left: left + "px",
|
|
9425
|
-
top: top + "px",
|
|
9426
|
-
// If user specified a center, set it
|
|
9427
|
-
// NOTE(kevinb): jQuery doesn't like that `transformOrigin `could be `null`
|
|
9428
|
-
// so we cast to `any` here.
|
|
9429
|
-
transformOrigin: center ? width / 2 + center[0] + "px " + (height / 2 + center[1]) + "px" : null
|
|
9522
|
+
const processed = {};
|
|
9523
|
+
Object.entries(attrs || {}).forEach(function ([key, value]) {
|
|
9524
|
+
const transformer = transformers[key];
|
|
9525
|
+
if (typeof transformer === "function") {
|
|
9526
|
+
Object.assign(processed, transformer(value));
|
|
9527
|
+
} else {
|
|
9528
|
+
const dasherized = String(key).replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase();
|
|
9529
|
+
processed[dasherized] = value;
|
|
9530
|
+
}
|
|
9430
9531
|
});
|
|
9431
|
-
|
|
9432
|
-
// Create Raphael canvas
|
|
9433
|
-
const localRaphael = raphael.exports(wrapper, width, height);
|
|
9434
|
-
|
|
9435
|
-
// Calculate path
|
|
9436
|
-
const visibleShape = localRaphael.path(createPath(points));
|
|
9437
|
-
return {
|
|
9438
|
-
wrapper: wrapper,
|
|
9439
|
-
visibleShape: visibleShape
|
|
9440
|
-
};
|
|
9532
|
+
return processed;
|
|
9441
9533
|
}
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9534
|
+
}
|
|
9535
|
+
const labelDirections = {
|
|
9536
|
+
center: [-0.5, -0.5],
|
|
9537
|
+
above: [-0.5, -1.0],
|
|
9538
|
+
"above right": [0.0, -1.0],
|
|
9539
|
+
right: [0.0, -0.5],
|
|
9540
|
+
"below right": [0.0, 0.0],
|
|
9541
|
+
below: [-0.5, 0.0],
|
|
9542
|
+
"below left": [-1.0, 0.0],
|
|
9543
|
+
left: [-1.0, -0.5],
|
|
9544
|
+
"above left": [-1.0, -1.0]
|
|
9545
|
+
};
|
|
9546
|
+
|
|
9547
|
+
// A range for a Graphie can be specified in one of three forms:
|
|
9548
|
+
// - A pair of intervals, [[xMin, xMax], [yMin, yMax]]. This is the
|
|
9549
|
+
// "normalized" form.
|
|
9550
|
+
// - A pair of magnitudes for the x and y ranges, respectively. E.g. [5, 10]
|
|
9551
|
+
// is equivalent to [[-5, 5], [-10, 10]].
|
|
9552
|
+
// - A single magnitude to be used for both x and y. E.g. 10 is equivalent to
|
|
9553
|
+
// [[-10, 10], [-10, 10]]
|
|
9554
|
+
// Overload normalizeRange to specify that it only returns undefined if its
|
|
9555
|
+
// argument is undefined.
|
|
9556
|
+
// normalizeRange converts a RangeSpecifier into a pair of Intervals that give
|
|
9557
|
+
// the x and y ranges, respectively
|
|
9558
|
+
function normalizeRange(range) {
|
|
9559
|
+
function normalizeInterval(magnitude) {
|
|
9560
|
+
if (typeof magnitude === "number") {
|
|
9561
|
+
return [-magnitude, magnitude];
|
|
9562
|
+
} else {
|
|
9563
|
+
return magnitude;
|
|
9564
|
+
}
|
|
9446
9565
|
}
|
|
9447
|
-
function
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9566
|
+
function getXAndYRanges(range) {
|
|
9567
|
+
if (Array.isArray(range)) {
|
|
9568
|
+
return range;
|
|
9569
|
+
} else {
|
|
9570
|
+
return [range, range];
|
|
9451
9571
|
}
|
|
9452
|
-
return l;
|
|
9453
9572
|
}
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
return thisGraphie.raphael.path(thisGraphie.svgParabolaPath(a, b, c));
|
|
9573
|
+
if (range == null) {
|
|
9574
|
+
return range;
|
|
9457
9575
|
}
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
|
|
9461
|
-
|
|
9462
|
-
|
|
9463
|
-
|
|
9464
|
-
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
const extraOffset = [Math.min(start[0], end[0]), Math.min(start[1], end[1])];
|
|
9468
|
-
|
|
9469
|
-
// Apply padding and offset to start, end points
|
|
9470
|
-
start = vector$1.add(vector$1.subtract(start, extraOffset), vector$1.scale(padding, 0.5));
|
|
9471
|
-
end = vector$1.add(vector$1.subtract(end, extraOffset), vector$1.scale(padding, 0.5));
|
|
9472
|
-
|
|
9473
|
-
// Calculate <div> dimensions
|
|
9474
|
-
const left = extraOffset[0] - padding[0] / 2;
|
|
9475
|
-
const top = extraOffset[1] - padding[1] / 2;
|
|
9476
|
-
const width = Math.abs(start[0] - end[0]) + padding[0];
|
|
9477
|
-
const height = Math.abs(start[1] - end[1]) + padding[1];
|
|
9576
|
+
const [xRange, yRange] = getXAndYRanges(range);
|
|
9577
|
+
return [normalizeInterval(xRange), normalizeInterval(yRange)];
|
|
9578
|
+
}
|
|
9579
|
+
function toPair(x) {
|
|
9580
|
+
if (Array.isArray(x)) {
|
|
9581
|
+
return x;
|
|
9582
|
+
}
|
|
9583
|
+
return [x, x];
|
|
9584
|
+
}
|
|
9478
9585
|
|
|
9479
|
-
|
|
9480
|
-
|
|
9481
|
-
|
|
9482
|
-
|
|
9483
|
-
|
|
9484
|
-
|
|
9485
|
-
|
|
9486
|
-
|
|
9487
|
-
|
|
9488
|
-
|
|
9489
|
-
|
|
9586
|
+
/**
|
|
9587
|
+
* Safari applies some SVG-specific styles to things that are not SVGs, so we
|
|
9588
|
+
* need to exclude those styles from things that are not SVGs.
|
|
9589
|
+
*
|
|
9590
|
+
* To see this behavior in action, open https://codepen.io/anon/pen/zENEoa in
|
|
9591
|
+
* Safari.
|
|
9592
|
+
*
|
|
9593
|
+
* Usage `{...someStyles, ...SVG_SPECIFIC_STYLE_MASK}`
|
|
9594
|
+
*/
|
|
9595
|
+
const SVG_SPECIFIC_STYLE_MASK = {
|
|
9596
|
+
"stroke-width": null
|
|
9597
|
+
};
|
|
9598
|
+
const setLabelMargins = function setLabelMargins(span, size) {
|
|
9599
|
+
const $span = $(span);
|
|
9600
|
+
const direction = $span.data("labelDirection");
|
|
9601
|
+
let [width, height] = size;
|
|
9602
|
+
// This can happen when a span
|
|
9603
|
+
// is invisible but we still want to update the CSS. At worst, we will
|
|
9604
|
+
// be off by a few pixels instead of in a different position entirely.
|
|
9605
|
+
if (width === 0 && height === 0) {
|
|
9606
|
+
[width, height] = [1, 1];
|
|
9607
|
+
Log.log("Label size was 0x0 in graphie.js; using 1x1 instead");
|
|
9608
|
+
}
|
|
9609
|
+
$span.css("visibility", "");
|
|
9610
|
+
if (typeof direction === "number") {
|
|
9611
|
+
const x = Math.cos(direction);
|
|
9612
|
+
const y = Math.sin(direction);
|
|
9613
|
+
const scale = Math.min(width / 2 / Math.abs(x), height / 2 / Math.abs(y));
|
|
9614
|
+
$span.css({
|
|
9615
|
+
marginLeft: -width / 2 + x * scale,
|
|
9616
|
+
marginTop: -height / 2 - y * scale
|
|
9617
|
+
});
|
|
9618
|
+
} else {
|
|
9619
|
+
const multipliers = labelDirections[direction || "center"];
|
|
9620
|
+
$span.css({
|
|
9621
|
+
marginLeft: Math.round(width * multipliers[0]),
|
|
9622
|
+
marginTop: Math.round(height * multipliers[1])
|
|
9490
9623
|
});
|
|
9491
|
-
|
|
9492
|
-
// Create Raphael canvas
|
|
9493
|
-
const localRaphael = raphael.exports(wrapper, width, height);
|
|
9494
|
-
|
|
9495
|
-
// Calculate path
|
|
9496
|
-
const path = "M" + start[0] + " " + start[1] + " " + "L" + end[0] + " " + end[1];
|
|
9497
|
-
const visibleShape = localRaphael.path(path);
|
|
9498
|
-
visibleShape.graphiePath = [start, end];
|
|
9499
|
-
return {
|
|
9500
|
-
wrapper: wrapper,
|
|
9501
|
-
visibleShape: visibleShape
|
|
9502
|
-
};
|
|
9503
9624
|
}
|
|
9504
|
-
|
|
9505
|
-
|
|
9506
|
-
|
|
9507
|
-
|
|
9508
|
-
|
|
9509
|
-
|
|
9510
|
-
|
|
9511
|
-
|
|
9512
|
-
|
|
9513
|
-
|
|
9514
|
-
|
|
9515
|
-
let y = step[1] * Math.ceil(yr[0] / step[1]);
|
|
9516
|
-
for (; y <= yr[1]; y += step[1]) {
|
|
9517
|
-
set.push(line([xr[0], y], [xr[1], y]));
|
|
9518
|
-
}
|
|
9519
|
-
return set;
|
|
9520
|
-
}
|
|
9521
|
-
function label(point, text, direction, latex) {
|
|
9522
|
-
latex = typeof latex === "undefined" || latex;
|
|
9523
|
-
const $span = $("<span>").addClass("graphie-label");
|
|
9524
|
-
const pad = thisGraphie.currentStyle["label-distance"];
|
|
9525
|
-
$span.css($.extend({}, {
|
|
9526
|
-
position: "absolute",
|
|
9527
|
-
padding: (pad != null ? pad : 7) + "px",
|
|
9528
|
-
color: "black"
|
|
9529
|
-
})).data("labelDirection", direction).appendTo(thisGraphie.el);
|
|
9530
|
-
|
|
9531
|
-
// @ts-expect-error - TS2339 - Property 'setPosition' does not exist on type 'JQuery<HTMLElement>'.
|
|
9532
|
-
$span.setPosition = function (point) {
|
|
9533
|
-
const scaledPoint = thisGraphie.scalePoint(point);
|
|
9534
|
-
$span.css({
|
|
9535
|
-
left: scaledPoint[0],
|
|
9536
|
-
top: scaledPoint[1]
|
|
9537
|
-
});
|
|
9538
|
-
};
|
|
9539
|
-
|
|
9540
|
-
// @ts-expect-error - TS2339 - Property 'setPosition' does not exist on type 'JQuery<HTMLElement>'.
|
|
9541
|
-
$span.setPosition(point);
|
|
9542
|
-
const span = $span[0];
|
|
9543
|
-
|
|
9544
|
-
// @ts-expect-error - TS2339 - Property 'processMath' does not exist on type 'JQuery<HTMLElement>'.
|
|
9545
|
-
$span.processMath = function (math, force) {
|
|
9546
|
-
processMath$1(span, math, force, function () {
|
|
9547
|
-
const width = span.scrollWidth;
|
|
9548
|
-
const height = span.scrollHeight;
|
|
9549
|
-
setLabelMargins(span, [width, height]);
|
|
9550
|
-
});
|
|
9551
|
-
};
|
|
9552
|
-
|
|
9553
|
-
// @ts-expect-error - TS2339 - Property 'processText' does not exist on type 'JQuery<HTMLElement>'.
|
|
9554
|
-
$span.processText = function (text) {
|
|
9555
|
-
$span.html(text);
|
|
9556
|
-
const width = span.scrollWidth;
|
|
9557
|
-
const height = span.scrollHeight;
|
|
9558
|
-
setLabelMargins(span, [width, height]);
|
|
9559
|
-
};
|
|
9560
|
-
if (latex) {
|
|
9561
|
-
// @ts-expect-error - TS2339 - Property 'processMath' does not exist on type 'JQuery<HTMLElement>'.
|
|
9562
|
-
$span.processMath(text, /* force */false);
|
|
9563
|
-
} else {
|
|
9564
|
-
// @ts-expect-error - TS2339 - Property 'processText' does not exist on type 'JQuery<HTMLElement>'.
|
|
9565
|
-
$span.processText(text);
|
|
9566
|
-
}
|
|
9567
|
-
return $span;
|
|
9568
|
-
}
|
|
9569
|
-
function plotParametric(fn, range, shade, fn2 = t => [t, 0]) {
|
|
9570
|
-
// Note: fn2 should only be set if 'shade' is true, as it denotes
|
|
9571
|
-
// the function between which fn should have its area shaded.
|
|
9572
|
-
// In general, plotParametric shouldn't be used to shade the area
|
|
9573
|
-
// between two arbitrary parametrics functions over an interval,
|
|
9574
|
-
// as the method assumes that fn and fn2 are both of the form
|
|
9575
|
-
// fn(t) = (t, fn'(t)) for some initial fn'.
|
|
9576
|
-
|
|
9577
|
-
// We truncate to 500,000, since anything bigger causes
|
|
9578
|
-
// overflow in the firefox svg renderer. This is safe
|
|
9579
|
-
// since 500,000 is outside the viewport anyway. We
|
|
9580
|
-
// write these functions the way we do to handle undefined.
|
|
9581
|
-
const clipper = xy => {
|
|
9582
|
-
if (Math.abs(xy[1]) > 500000) {
|
|
9583
|
-
return [xy[0], Math.min(Math.max(xy[1], -500000), 500000)];
|
|
9584
|
-
}
|
|
9585
|
-
return xy;
|
|
9586
|
-
};
|
|
9587
|
-
const clippedFn = x => clipper(fn(x));
|
|
9588
|
-
const clippedFn2 = x => clipper(fn2(x));
|
|
9589
|
-
if (!thisGraphie.currentStyle.strokeLinejoin) {
|
|
9590
|
-
thisGraphie.currentStyle.strokeLinejoin = "round";
|
|
9591
|
-
}
|
|
9592
|
-
if (!thisGraphie.currentStyle.strokeLinecap) {
|
|
9593
|
-
thisGraphie.currentStyle.strokeLinecap = "round";
|
|
9594
|
-
}
|
|
9595
|
-
const min = range[0];
|
|
9596
|
-
const max = range[1];
|
|
9597
|
-
let step = (max - min) / (thisGraphie.currentStyle["plot-points"] || 800);
|
|
9598
|
-
if (step === 0) {
|
|
9599
|
-
step = 1;
|
|
9600
|
-
}
|
|
9601
|
-
const paths = thisGraphie.raphael.set();
|
|
9602
|
-
let points = [];
|
|
9603
|
-
let lastDiff = GraphUtils.coordDiff(clippedFn(min), clippedFn2(min));
|
|
9604
|
-
let lastFlip = min;
|
|
9605
|
-
for (let t = min; t <= max; t += step) {
|
|
9606
|
-
const top = clippedFn(t);
|
|
9607
|
-
const bottom = clippedFn2(t);
|
|
9608
|
-
const diff = GraphUtils.coordDiff(top, bottom);
|
|
9609
|
-
|
|
9610
|
-
// Find points where it flips
|
|
9611
|
-
// Create path that sketches area between the two functions
|
|
9612
|
-
if (
|
|
9613
|
-
// if there is an asymptote here, meaning that the graph
|
|
9614
|
-
// switches signs and has a large difference
|
|
9615
|
-
diff[1] < 0 !== lastDiff[1] < 0 && Math.abs(diff[1] - lastDiff[1]) > 2 * thisGraphie.drawingTransform().pixelsPerUnitY() ||
|
|
9616
|
-
// or the function is undefined
|
|
9617
|
-
isNaN(diff[1])) {
|
|
9618
|
-
// split the path at this point, and draw it
|
|
9619
|
-
if (shade) {
|
|
9620
|
-
// @ts-expect-error - TS2345 - Argument of type 'any' is not assignable to parameter of type 'never'.
|
|
9621
|
-
points.push(top);
|
|
9622
|
-
|
|
9623
|
-
// backtrack to draw paired function
|
|
9624
|
-
for (let u = t - step; u >= lastFlip; u -= step) {
|
|
9625
|
-
// @ts-expect-error - TS2345 - Argument of type 'any' is not assignable to parameter of type 'never'.
|
|
9626
|
-
points.push(clippedFn2(u));
|
|
9627
|
-
}
|
|
9628
|
-
lastFlip = t;
|
|
9629
|
-
}
|
|
9630
|
-
paths.push(path(points));
|
|
9631
|
-
// restart the path, excluding this point
|
|
9632
|
-
points = [];
|
|
9633
|
-
if (shade) {
|
|
9634
|
-
// @ts-expect-error - TS2345 - Argument of type 'any' is not assignable to parameter of type 'never'.
|
|
9635
|
-
points.push(top);
|
|
9636
|
-
}
|
|
9637
|
-
} else {
|
|
9638
|
-
// otherwise, just add the point to the path
|
|
9639
|
-
// @ts-expect-error - TS2345 - Argument of type 'any' is not assignable to parameter of type 'never'.
|
|
9640
|
-
points.push(top);
|
|
9641
|
-
}
|
|
9642
|
-
lastDiff = diff;
|
|
9625
|
+
};
|
|
9626
|
+
const GraphUtils = {
|
|
9627
|
+
Graphie: Graphie$1,
|
|
9628
|
+
createGraphie: function (el) {
|
|
9629
|
+
return new Graphie$1(el);
|
|
9630
|
+
},
|
|
9631
|
+
unscaledSvgPath: function (points) {
|
|
9632
|
+
// If this is an empty closed path, return "" instead of "z", which
|
|
9633
|
+
// would give an error
|
|
9634
|
+
if (points[0] === true) {
|
|
9635
|
+
return "";
|
|
9643
9636
|
}
|
|
9644
|
-
|
|
9645
|
-
|
|
9646
|
-
|
|
9647
|
-
// @ts-expect-error - TS2345 - Argument of type 'any' is not assignable to parameter of type 'never'.
|
|
9648
|
-
points.push(clippedFn2(u));
|
|
9637
|
+
return points.map(function (point, i) {
|
|
9638
|
+
if (point === true) {
|
|
9639
|
+
return "z";
|
|
9649
9640
|
}
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
|
|
9653
|
-
|
|
9654
|
-
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
|
|
9663
|
-
|
|
9641
|
+
return (i === 0 ? "M" : "L") + point[0] + " " + point[1];
|
|
9642
|
+
}).join("");
|
|
9643
|
+
},
|
|
9644
|
+
getDistance: function (point1, point2) {
|
|
9645
|
+
return point.distanceToPoint(point1, point2);
|
|
9646
|
+
},
|
|
9647
|
+
/**
|
|
9648
|
+
* Round the given coordinates to a given snap value
|
|
9649
|
+
* (e.g., nearest 0.2 increment)
|
|
9650
|
+
*/
|
|
9651
|
+
snapCoord: function (coord, snap) {
|
|
9652
|
+
return coord.map(function (val, i) {
|
|
9653
|
+
return KhanMath.roundToNearest(snap[i], val);
|
|
9654
|
+
});
|
|
9655
|
+
},
|
|
9656
|
+
// Find the angle in degrees between two or three points
|
|
9657
|
+
findAngle: function (point1, point2, vertex) {
|
|
9658
|
+
if (vertex === undefined) {
|
|
9659
|
+
const x = point1[0] - point2[0];
|
|
9660
|
+
const y = point1[1] - point2[1];
|
|
9661
|
+
if (!x && !y) {
|
|
9662
|
+
return 0;
|
|
9664
9663
|
}
|
|
9665
|
-
return
|
|
9666
|
-
return [fn(y), y];
|
|
9667
|
-
}, range, shade);
|
|
9664
|
+
return (180 + Math.atan2(-y, -x) * 180 / Math.PI + 360) % 360;
|
|
9668
9665
|
}
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
return [x, fn(x)];
|
|
9673
|
-
}, range, shade, function (x) {
|
|
9674
|
-
return [x, fn2(x)];
|
|
9675
|
-
});
|
|
9676
|
-
}
|
|
9677
|
-
throw new PerseusError("fn2 should only be set when 'shade' is True.", Errors.Internal);
|
|
9678
|
-
}
|
|
9679
|
-
return plotParametric(function (x) {
|
|
9680
|
-
return [x, fn(x)];
|
|
9681
|
-
}, range, shade);
|
|
9682
|
-
}
|
|
9683
|
-
const drawingTools = {
|
|
9684
|
-
circle,
|
|
9685
|
-
rect,
|
|
9686
|
-
ellipse,
|
|
9687
|
-
fixedEllipse,
|
|
9688
|
-
arc,
|
|
9689
|
-
path,
|
|
9690
|
-
fixedPath,
|
|
9691
|
-
scaledPath,
|
|
9692
|
-
line,
|
|
9693
|
-
parabola,
|
|
9694
|
-
fixedLine,
|
|
9695
|
-
sinusoid,
|
|
9696
|
-
grid,
|
|
9697
|
-
label,
|
|
9698
|
-
plotParametric,
|
|
9699
|
-
plot
|
|
9700
|
-
};
|
|
9701
|
-
function graphify(drawingFn) {
|
|
9702
|
-
return function (...args) {
|
|
9703
|
-
const last = args[args.length - 1];
|
|
9704
|
-
const oldStyle = thisGraphie.currentStyle;
|
|
9705
|
-
let result;
|
|
9706
|
-
|
|
9707
|
-
// The last argument is probably trying to change the style
|
|
9708
|
-
if (typeof last === "object" && !_.isArray(last)) {
|
|
9709
|
-
thisGraphie.currentStyle = _extends({}, thisGraphie.currentStyle, thisGraphie.processAttributes(last));
|
|
9710
|
-
const rest = [].slice.call(args, 0, args.length - 1);
|
|
9711
|
-
result = drawingFn(...rest);
|
|
9712
|
-
} else {
|
|
9713
|
-
thisGraphie.currentStyle = $.extend({}, thisGraphie.currentStyle);
|
|
9714
|
-
result = drawingFn(...args);
|
|
9715
|
-
}
|
|
9716
|
-
|
|
9717
|
-
// Bad heuristic for recognizing Raphael elements and sets
|
|
9718
|
-
const type = result.constructor.prototype;
|
|
9719
|
-
if (type === raphael.exports.el || type === raphael.exports.st) {
|
|
9720
|
-
result.attr(thisGraphie.currentStyle);
|
|
9721
|
-
if (thisGraphie.currentStyle.arrows) {
|
|
9722
|
-
result = addArrowheads(result);
|
|
9723
|
-
}
|
|
9724
|
-
} else if (result instanceof $) {
|
|
9725
|
-
// We assume that if it's not a Raphael element/set, it
|
|
9726
|
-
// does not contain SVG.
|
|
9727
|
-
// @ts-expect-error - TS2339 - Property 'css' does not exist on type '{}'.
|
|
9728
|
-
result.css(_extends({}, thisGraphie.currentStyle, SVG_SPECIFIC_STYLE_MASK));
|
|
9729
|
-
}
|
|
9730
|
-
thisGraphie.currentStyle = oldStyle;
|
|
9731
|
-
return result;
|
|
9732
|
-
};
|
|
9733
|
-
}
|
|
9734
|
-
$.each(drawingTools, function (name) {
|
|
9735
|
-
thisGraphie[name] = graphify(drawingTools[name]);
|
|
9736
|
-
});
|
|
9737
|
-
return thisGraphie;
|
|
9666
|
+
return GraphUtils.findAngle(point1, vertex) - GraphUtils.findAngle(point2, vertex);
|
|
9667
|
+
},
|
|
9668
|
+
graphs: {}
|
|
9738
9669
|
};
|
|
9739
9670
|
|
|
9740
9671
|
/* eslint-disable */
|
|
@@ -10452,15 +10383,65 @@ const WrappedPath = function WrappedPath(graphie, points, options) {
|
|
|
10452
10383
|
};
|
|
10453
10384
|
_.extend(WrappedPath.prototype, WrappedDefaults);
|
|
10454
10385
|
|
|
10455
|
-
|
|
10386
|
+
/**
|
|
10387
|
+
* A collection of geomtry-related utility functions
|
|
10388
|
+
*/
|
|
10456
10389
|
const {
|
|
10457
|
-
|
|
10458
|
-
|
|
10459
|
-
|
|
10460
|
-
|
|
10461
|
-
|
|
10462
|
-
|
|
10390
|
+
eq: eq$1,
|
|
10391
|
+
deepEq: deepEq$4
|
|
10392
|
+
} = Util;
|
|
10393
|
+
|
|
10394
|
+
// This should really be a readonly tuple of [number, number]
|
|
10395
|
+
|
|
10396
|
+
// a, b, c
|
|
10397
|
+
|
|
10398
|
+
function sign(val) {
|
|
10399
|
+
if (eq$1(val, 0)) {
|
|
10400
|
+
return 0;
|
|
10401
|
+
}
|
|
10402
|
+
return val > 0 ? 1 : -1;
|
|
10403
|
+
}
|
|
10404
|
+
function ccw(a, b, c) {
|
|
10405
|
+
return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
|
|
10406
|
+
}
|
|
10407
|
+
function collinear(a, b, c) {
|
|
10408
|
+
return eq$1(ccw(a, b, c), 0);
|
|
10463
10409
|
}
|
|
10410
|
+
|
|
10411
|
+
// Given rect bounding points A and B, whether point C is inside the rect
|
|
10412
|
+
function pointInRect(a, b, c) {
|
|
10413
|
+
return c[0] <= Math.max(a[0], b[0]) && c[0] >= Math.min(a[0], b[0]) && c[1] <= Math.max(a[1], b[1]) && c[1] >= Math.min(a[1], b[1]);
|
|
10414
|
+
}
|
|
10415
|
+
|
|
10416
|
+
// Whether line segment AB intersects line segment CD
|
|
10417
|
+
// http://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
|
|
10418
|
+
function intersects(ab, cd) {
|
|
10419
|
+
const triplets = [[ab[0], ab[1], cd[0]], [ab[0], ab[1], cd[1]], [cd[0], cd[1], ab[0]], [cd[0], cd[1], ab[1]]];
|
|
10420
|
+
const orientations = _.map(triplets, function (triplet) {
|
|
10421
|
+
return sign(ccw(...triplet));
|
|
10422
|
+
});
|
|
10423
|
+
if (orientations[0] !== orientations[1] && orientations[2] !== orientations[3]) {
|
|
10424
|
+
return true;
|
|
10425
|
+
}
|
|
10426
|
+
for (let i = 0; i < 4; i++) {
|
|
10427
|
+
if (orientations[i] === 0 && pointInRect(...triplets[i])) {
|
|
10428
|
+
return true;
|
|
10429
|
+
}
|
|
10430
|
+
}
|
|
10431
|
+
return false;
|
|
10432
|
+
}
|
|
10433
|
+
function vector(a, b) {
|
|
10434
|
+
return _.map(_.zip(a, b), function (pair) {
|
|
10435
|
+
return pair[0] - pair[1];
|
|
10436
|
+
});
|
|
10437
|
+
}
|
|
10438
|
+
function reverseVector(vector) {
|
|
10439
|
+
return [-vector[0], -vector[1]];
|
|
10440
|
+
}
|
|
10441
|
+
|
|
10442
|
+
// Returns whether connecting the given sequence of `points` forms a clockwise
|
|
10443
|
+
// path (assuming a closed loop, where the last point connects back to the
|
|
10444
|
+
// first).
|
|
10464
10445
|
function clockwise$1(points) {
|
|
10465
10446
|
const segments = _.zip(points, points.slice(1).concat(points.slice(0, 1)));
|
|
10466
10447
|
const areas = _.map(segments, function (segment) {
|
|
@@ -10470,17 +10451,164 @@ function clockwise$1(points) {
|
|
|
10470
10451
|
});
|
|
10471
10452
|
return sum$1(areas) > 0;
|
|
10472
10453
|
}
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
|
|
10454
|
+
function magnitude(v) {
|
|
10455
|
+
return Math.sqrt(_.reduce(v, function (memo, el) {
|
|
10456
|
+
// @ts-expect-error - TS2345 - Argument of type 'Coord' is not assignable to parameter of type 'number'.
|
|
10457
|
+
return memo + Math.pow(el, 2);
|
|
10458
|
+
}, 0));
|
|
10478
10459
|
}
|
|
10479
|
-
function
|
|
10480
|
-
return _.
|
|
10481
|
-
return
|
|
10460
|
+
function dotProduct(a, b) {
|
|
10461
|
+
return _.reduce(_.zip(a, b), function (memo, pair) {
|
|
10462
|
+
return memo + pair[0] * pair[1];
|
|
10463
|
+
}, 0);
|
|
10464
|
+
}
|
|
10465
|
+
function sideLengths(coords) {
|
|
10466
|
+
const segments = _.zip(coords, rotate(coords));
|
|
10467
|
+
return segments.map(function (segment) {
|
|
10468
|
+
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
10469
|
+
return magnitude(vector(...segment));
|
|
10482
10470
|
});
|
|
10483
10471
|
}
|
|
10472
|
+
|
|
10473
|
+
// Based on http://math.stackexchange.com/a/151149
|
|
10474
|
+
function angleMeasures(coords) {
|
|
10475
|
+
const triplets = _.zip(rotate(coords, -1), coords, rotate(coords, 1));
|
|
10476
|
+
const offsets = _.map(triplets, function (triplet) {
|
|
10477
|
+
const p = vector(triplet[1], triplet[0]);
|
|
10478
|
+
const q = vector(triplet[2], triplet[1]);
|
|
10479
|
+
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'Coord'. | TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
10480
|
+
const raw = Math.acos(dotProduct(p, q) / (magnitude(p) * magnitude(q)));
|
|
10481
|
+
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
10482
|
+
return sign(ccw(...triplet)) > 0 ? raw : -raw;
|
|
10483
|
+
});
|
|
10484
|
+
const sum = _.reduce(offsets, function (memo, arg) {
|
|
10485
|
+
return memo + arg;
|
|
10486
|
+
}, 0);
|
|
10487
|
+
return _.map(offsets, function (offset) {
|
|
10488
|
+
return sum > 0 ? Math.PI - offset : Math.PI + offset;
|
|
10489
|
+
});
|
|
10490
|
+
}
|
|
10491
|
+
|
|
10492
|
+
// Whether two polygons are similar (or if specified, congruent)
|
|
10493
|
+
function similar(coords1, coords2, tolerance) {
|
|
10494
|
+
if (coords1.length !== coords2.length) {
|
|
10495
|
+
return false;
|
|
10496
|
+
}
|
|
10497
|
+
const n = coords1.length;
|
|
10498
|
+
const angles1 = angleMeasures(coords1);
|
|
10499
|
+
const angles2 = angleMeasures(coords2);
|
|
10500
|
+
const sides1 = sideLengths(coords1);
|
|
10501
|
+
const sides2 = sideLengths(coords2);
|
|
10502
|
+
for (let i = 0; i < 2 * n; i++) {
|
|
10503
|
+
let angles = angles2.slice();
|
|
10504
|
+
let sides = sides2.slice();
|
|
10505
|
+
|
|
10506
|
+
// Reverse angles and sides to allow matching reflected polygons
|
|
10507
|
+
if (i >= n) {
|
|
10508
|
+
angles.reverse();
|
|
10509
|
+
sides.reverse();
|
|
10510
|
+
// Since sides are calculated from two coordinates,
|
|
10511
|
+
// simply reversing results in an off by one error
|
|
10512
|
+
// @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
10513
|
+
sides = rotate(sides, 1);
|
|
10514
|
+
}
|
|
10515
|
+
|
|
10516
|
+
// @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
10517
|
+
angles = rotate(angles, i);
|
|
10518
|
+
// @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
10519
|
+
sides = rotate(sides, i);
|
|
10520
|
+
if (deepEq$4(angles1, angles)) {
|
|
10521
|
+
const sidePairs = _.zip(sides1, sides);
|
|
10522
|
+
const factors = _.map(sidePairs, function (pair) {
|
|
10523
|
+
return pair[0] / pair[1];
|
|
10524
|
+
});
|
|
10525
|
+
const same = _.all(factors, function (factor) {
|
|
10526
|
+
return eq$1(factors[0], factor);
|
|
10527
|
+
});
|
|
10528
|
+
const congruentEnough = _.all(sidePairs, function (pair) {
|
|
10529
|
+
return number.equal(pair[0], pair[1], tolerance);
|
|
10530
|
+
});
|
|
10531
|
+
if (same && congruentEnough) {
|
|
10532
|
+
return true;
|
|
10533
|
+
}
|
|
10534
|
+
}
|
|
10535
|
+
}
|
|
10536
|
+
return false;
|
|
10537
|
+
}
|
|
10538
|
+
|
|
10539
|
+
// Given triangle with sides ABC return angle opposite side C in degrees
|
|
10540
|
+
function lawOfCosines(a, b, c) {
|
|
10541
|
+
return Math.acos((a * a + b * b - c * c) / (2 * a * b)) * 180 / Math.PI;
|
|
10542
|
+
}
|
|
10543
|
+
function canonicalSineCoefficients$1([amplitude, angularFrequency, phase, verticalOffset]) {
|
|
10544
|
+
// For a curve of the form f(x) = a * Sin(b * x - c) + d,
|
|
10545
|
+
// this function ensures that a, b > 0, and c is its
|
|
10546
|
+
// smallest possible positive value.
|
|
10547
|
+
|
|
10548
|
+
// Guarantee a > 0
|
|
10549
|
+
if (amplitude < 0) {
|
|
10550
|
+
amplitude *= -1;
|
|
10551
|
+
angularFrequency *= -1;
|
|
10552
|
+
phase *= -1;
|
|
10553
|
+
}
|
|
10554
|
+
const period = 2 * Math.PI;
|
|
10555
|
+
// Guarantee b > 0
|
|
10556
|
+
if (angularFrequency < 0) {
|
|
10557
|
+
angularFrequency *= -1;
|
|
10558
|
+
phase *= -1;
|
|
10559
|
+
phase += period / 2;
|
|
10560
|
+
}
|
|
10561
|
+
|
|
10562
|
+
// Guarantee c is smallest possible positive value
|
|
10563
|
+
while (phase > 0) {
|
|
10564
|
+
phase -= period;
|
|
10565
|
+
}
|
|
10566
|
+
while (phase < 0) {
|
|
10567
|
+
phase += period;
|
|
10568
|
+
}
|
|
10569
|
+
return [amplitude, angularFrequency, phase, verticalOffset];
|
|
10570
|
+
}
|
|
10571
|
+
|
|
10572
|
+
// e.g. rotate([1, 2, 3]) -> [2, 3, 1]
|
|
10573
|
+
function rotate(array, n) {
|
|
10574
|
+
n = typeof n === "undefined" ? 1 : n % array.length;
|
|
10575
|
+
return array.slice(n).concat(array.slice(0, n));
|
|
10576
|
+
}
|
|
10577
|
+
function getLineEquation(first, second) {
|
|
10578
|
+
if (eq$1(first[0], second[0])) {
|
|
10579
|
+
return "x = " + first[0].toFixed(3);
|
|
10580
|
+
}
|
|
10581
|
+
const m = (second[1] - first[1]) / (second[0] - first[0]);
|
|
10582
|
+
const b = first[1] - m * first[0];
|
|
10583
|
+
return "y = " + m.toFixed(3) + "x + " + b.toFixed(3);
|
|
10584
|
+
}
|
|
10585
|
+
|
|
10586
|
+
// Stolen from the wikipedia article
|
|
10587
|
+
// http://en.wikipedia.org/wiki/Line-line_intersection
|
|
10588
|
+
function getLineIntersection(
|
|
10589
|
+
// TODO(LP-10725): update these to be 2-tuples
|
|
10590
|
+
firstPoints, secondPoints) {
|
|
10591
|
+
const x1 = firstPoints[0][0];
|
|
10592
|
+
const y1 = firstPoints[0][1];
|
|
10593
|
+
const x2 = firstPoints[1][0];
|
|
10594
|
+
const y2 = firstPoints[1][1];
|
|
10595
|
+
const x3 = secondPoints[0][0];
|
|
10596
|
+
const y3 = secondPoints[0][1];
|
|
10597
|
+
const x4 = secondPoints[1][0];
|
|
10598
|
+
const y4 = secondPoints[1][1];
|
|
10599
|
+
const determinant = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
|
10600
|
+
if (Math.abs(determinant) < 1e-9) {
|
|
10601
|
+
return "Lines are parallel";
|
|
10602
|
+
}
|
|
10603
|
+
const x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / determinant;
|
|
10604
|
+
const y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / determinant;
|
|
10605
|
+
return "Intersection: (" + x.toFixed(3) + ", " + y.toFixed(3) + ")";
|
|
10606
|
+
}
|
|
10607
|
+
|
|
10608
|
+
/* eslint-disable @babel/no-invalid-this */
|
|
10609
|
+
const {
|
|
10610
|
+
getCanUse3dTransform
|
|
10611
|
+
} = InteractiveUtil;
|
|
10484
10612
|
function scaledDistanceFromAngle(angle) {
|
|
10485
10613
|
const a = 3.51470560176242 * 20;
|
|
10486
10614
|
const b = 0.5687298702748785 * 20;
|
|
@@ -10657,10 +10785,10 @@ _.extend(GraphUtils.Graphie.prototype, {
|
|
|
10657
10785
|
let sRadius = sPadding + scaledDistanceFromAngle(angle);
|
|
10658
10786
|
const temp = [];
|
|
10659
10787
|
if (Math.abs(angle - 90) < 1e-9 && options.showRightAngleMarker) {
|
|
10660
|
-
const v1 =
|
|
10661
|
-
const v2 =
|
|
10788
|
+
const v1 = vector$1.add(sVertex, scaledPolarDeg(sRadius, startAngle));
|
|
10789
|
+
const v2 = vector$1.add(sVertex, scaledPolarDeg(sRadius, endAngle));
|
|
10662
10790
|
sRadius *= Math.SQRT2;
|
|
10663
|
-
const v3 =
|
|
10791
|
+
const v3 = vector$1.add(sVertex, scaledPolarDeg(sRadius, halfAngle));
|
|
10664
10792
|
_.each([v1, v2], function (v) {
|
|
10665
10793
|
// @ts-expect-error - TS2345 - Argument of type 'any' is not assignable to parameter of type 'never'.
|
|
10666
10794
|
temp.push(graphie.scaledPath([v, v3], options.style));
|
|
@@ -10681,7 +10809,7 @@ _.extend(GraphUtils.Graphie.prototype, {
|
|
|
10681
10809
|
text = text.replace(match[0], KhanMath.toFixedApprox(angle, precision) + "^{\\circ}");
|
|
10682
10810
|
}
|
|
10683
10811
|
const sOffset = scaledPolarDeg(sRadius + 15, halfAngle);
|
|
10684
|
-
const sPosition =
|
|
10812
|
+
const sPosition = vector$1.add(sVertex, sOffset);
|
|
10685
10813
|
const position = graphie.unscalePoint(sPosition);
|
|
10686
10814
|
|
|
10687
10815
|
// Reuse label if possible
|
|
@@ -10732,7 +10860,7 @@ _.extend(GraphUtils.Graphie.prototype, {
|
|
|
10732
10860
|
const sOffset = sSpacing * (i - (n - 1) / 2);
|
|
10733
10861
|
const sOffsetVector = scaledPolarRad(sOffset, parallelAngle);
|
|
10734
10862
|
const sHeightVector = scaledPolarRad(sHeight, perpendicularAngle);
|
|
10735
|
-
const sPath = [
|
|
10863
|
+
const sPath = [vector$1.add(sMidpoint, sOffsetVector, sHeightVector), vector$1.add(sMidpoint, sOffsetVector, reverseVector(sHeightVector))];
|
|
10736
10864
|
|
|
10737
10865
|
// @ts-expect-error - TS2345 - Argument of type 'any' is not assignable to parameter of type 'never'.
|
|
10738
10866
|
temp.push(graphie.scaledPath(sPath, style));
|
|
@@ -10759,7 +10887,7 @@ _.extend(GraphUtils.Graphie.prototype, {
|
|
|
10759
10887
|
if (start !== p1) {
|
|
10760
10888
|
sOffsetVector = reverseVector(sOffsetVector);
|
|
10761
10889
|
}
|
|
10762
|
-
const sEnd =
|
|
10890
|
+
const sEnd = vector$1.add(sMidpoint, sOffsetVector);
|
|
10763
10891
|
|
|
10764
10892
|
// @ts-expect-error - TS2345 - Argument of type 'any' is not assignable to parameter of type 'never'.
|
|
10765
10893
|
temp.push(graphie.scaledPath([sStart, sEnd], style));
|
|
@@ -10775,7 +10903,7 @@ _.extend(GraphUtils.Graphie.prototype, {
|
|
|
10775
10903
|
}
|
|
10776
10904
|
const sOffset = 20;
|
|
10777
10905
|
const sOffsetVector = scaledPolarRad(sOffset, perpendicularAngle);
|
|
10778
|
-
const sPosition =
|
|
10906
|
+
const sPosition = vector$1.add(sMidpoint, sOffsetVector);
|
|
10779
10907
|
const position = graphie.unscalePoint(sPosition);
|
|
10780
10908
|
|
|
10781
10909
|
// Reuse label if possible
|
|
@@ -10833,7 +10961,7 @@ _.extend(GraphUtils.Graphie.prototype, {
|
|
|
10833
10961
|
}
|
|
10834
10962
|
const sRadius = 10 + scaledDistanceFromAngle(360 - angle);
|
|
10835
10963
|
const sOffsetVector = scaledPolarDeg(sRadius, halfAngle);
|
|
10836
|
-
const sPosition =
|
|
10964
|
+
const sPosition = vector$1.add(sVertex, sOffsetVector);
|
|
10837
10965
|
const position = graphie.unscalePoint(sPosition);
|
|
10838
10966
|
|
|
10839
10967
|
// Reuse label if possible
|
|
@@ -12684,7 +12812,7 @@ _.extend(GraphUtils.Graphie.prototype, {
|
|
|
12684
12812
|
// @ts-expect-error - TS2350 - Only a void function can be called with the 'new' keyword.
|
|
12685
12813
|
return new Ruler(this, options || {});
|
|
12686
12814
|
},
|
|
12687
|
-
addPoints:
|
|
12815
|
+
addPoints: vector$1.add
|
|
12688
12816
|
});
|
|
12689
12817
|
function Protractor(graph, center) {
|
|
12690
12818
|
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
@@ -13038,7 +13166,7 @@ function Ruler(graphie, options) {
|
|
|
13038
13166
|
set.push(label);
|
|
13039
13167
|
}
|
|
13040
13168
|
});
|
|
13041
|
-
const mouseTarget = graphie.mouselayer.path(
|
|
13169
|
+
const mouseTarget = graphie.mouselayer.path(graphie.svgPath([leftBottom, [left, top], rightTop, [right, bottom], /* closed */true]));
|
|
13042
13170
|
mouseTarget.attr({
|
|
13043
13171
|
fill: "#000",
|
|
13044
13172
|
opacity: 0,
|
|
@@ -13282,12 +13410,12 @@ _.extend(MovableAngle.prototype, {
|
|
|
13282
13410
|
points[1].onMove = function (x, y) {
|
|
13283
13411
|
const oldVertex = points[1].coord;
|
|
13284
13412
|
const newVertex = [x, y];
|
|
13285
|
-
const delta =
|
|
13413
|
+
const delta = vector$1.add(newVertex, reverseVector(oldVertex));
|
|
13286
13414
|
let valid = true;
|
|
13287
13415
|
const newPoints = {};
|
|
13288
13416
|
_.each([0, 2], function (i) {
|
|
13289
13417
|
const oldPoint = points[i].coord;
|
|
13290
|
-
let newPoint =
|
|
13418
|
+
let newPoint = vector$1.add(oldPoint, delta);
|
|
13291
13419
|
let angle = GraphUtils.findAngle(newVertex, newPoint);
|
|
13292
13420
|
angle *= Math.PI / 180;
|
|
13293
13421
|
newPoint = graphie.constrainToBoundsOnAngle(newPoint, 10, angle);
|
|
@@ -13324,7 +13452,7 @@ _.extend(MovableAngle.prototype, {
|
|
|
13324
13452
|
let angle = GraphUtils.findAngle(newPoint, vertex);
|
|
13325
13453
|
angle = Math.round((angle - snapOffset) / snap) * snap + snapOffset;
|
|
13326
13454
|
const distance = GraphUtils.getDistance(newPoint, vertex);
|
|
13327
|
-
return
|
|
13455
|
+
return vector$1.add(vertex, polar(distance, angle));
|
|
13328
13456
|
}
|
|
13329
13457
|
return true;
|
|
13330
13458
|
};
|
|
@@ -13475,7 +13603,7 @@ _.extend(MovableAngle.prototype, {
|
|
|
13475
13603
|
|
|
13476
13604
|
/* eslint-disable @babel/no-invalid-this */
|
|
13477
13605
|
const nestedMap$1 = Util.nestedMap;
|
|
13478
|
-
const deepEq$
|
|
13606
|
+
const deepEq$3 = Util.deepEq;
|
|
13479
13607
|
|
|
13480
13608
|
/**
|
|
13481
13609
|
* A base class for all Graphie Movables
|
|
@@ -13572,7 +13700,7 @@ const createSimpleClass = function createSimpleClass(addFunction) {
|
|
|
13572
13700
|
this._prevProps = this.props;
|
|
13573
13701
|
},
|
|
13574
13702
|
modify: function (graphie) {
|
|
13575
|
-
if (!deepEq$
|
|
13703
|
+
if (!deepEq$3(this.props, this._prevProps)) {
|
|
13576
13704
|
this.remove();
|
|
13577
13705
|
this.add(graphie);
|
|
13578
13706
|
this._prevProps = this.props;
|
|
@@ -16029,7 +16157,7 @@ var Movables = {
|
|
|
16029
16157
|
const GraphieMovable = GraphieClasses.GraphieMovable;
|
|
16030
16158
|
const createGraphie = GraphUtils.createGraphie;
|
|
16031
16159
|
const {
|
|
16032
|
-
deepEq: deepEq$
|
|
16160
|
+
deepEq: deepEq$2,
|
|
16033
16161
|
nestedMap
|
|
16034
16162
|
} = Util;
|
|
16035
16163
|
const {
|
|
@@ -16275,7 +16403,7 @@ class Graphie extends React.Component {
|
|
|
16275
16403
|
if (this.props.setup !== prevProps.setup) {
|
|
16276
16404
|
Log.error("<Graphie> was given a new setup function. " + "This is a bad idea; please refactor your code to give " + "the same setup function reference to <Graphie> on " + "every render.", Errors.Internal);
|
|
16277
16405
|
}
|
|
16278
|
-
if (!deepEq$
|
|
16406
|
+
if (!deepEq$2(this.props.options, prevProps.options) || !deepEq$2(this.props.box, prevProps.box) || !deepEq$2(this.props.range, prevProps.range)) {
|
|
16279
16407
|
this._setupGraphie();
|
|
16280
16408
|
}
|
|
16281
16409
|
this._updateMovables();
|
|
@@ -18965,10 +19093,18 @@ class Renderer extends React.Component {
|
|
|
18965
19093
|
});
|
|
18966
19094
|
};
|
|
18967
19095
|
this._getAllWidgetsStartProps = (allWidgetInfo, props) => {
|
|
18968
|
-
|
|
18969
|
-
|
|
18970
|
-
|
|
19096
|
+
const {
|
|
19097
|
+
apiOptions,
|
|
19098
|
+
problemNum
|
|
19099
|
+
} = props;
|
|
19100
|
+
const widgetsStartProps = {};
|
|
19101
|
+
entries(allWidgetInfo).forEach(([key, widgetInfo]) => {
|
|
19102
|
+
widgetsStartProps[key] = getRendererPropsForWidgetInfo(widgetInfo, problemNum);
|
|
18971
19103
|
});
|
|
19104
|
+
|
|
19105
|
+
// Call the onWidgetStartProps callback if it exists
|
|
19106
|
+
apiOptions == null || apiOptions.onWidgetStartProps == null || apiOptions.onWidgetStartProps(widgetsStartProps);
|
|
19107
|
+
return widgetsStartProps;
|
|
18972
19108
|
};
|
|
18973
19109
|
// This is only used in _getWidgetInfo as a fallback if widgetId
|
|
18974
19110
|
// doesn't exist in this.state.widgetInfo. It doesn't get run as
|
|
@@ -25443,7 +25579,7 @@ const DEFAULT_BACKGROUND_IMAGE = {
|
|
|
25443
25579
|
|
|
25444
25580
|
// TODO(charlie): These really need to go into a utility file as they're being
|
|
25445
25581
|
// used by both interactive-graph and now grapher.
|
|
25446
|
-
function canonicalSineCoefficients
|
|
25582
|
+
function canonicalSineCoefficients(coeffs) {
|
|
25447
25583
|
// For a curve of the form f(x) = a * Sin(b * x - c) + d,
|
|
25448
25584
|
// this function ensures that a, b > 0, and c is its
|
|
25449
25585
|
// smallest possible positive value.
|
|
@@ -25624,7 +25760,7 @@ const Sinusoid = _.extend({}, PlotDefaults, {
|
|
|
25624
25760
|
return "y = " + a.toFixed(3) + " sin(" + b.toFixed(3) + "x - " + c.toFixed(3) + ") + " + d.toFixed(3);
|
|
25625
25761
|
},
|
|
25626
25762
|
areEqual: function (coeffs1, coeffs2) {
|
|
25627
|
-
return Util.deepEq(canonicalSineCoefficients
|
|
25763
|
+
return Util.deepEq(canonicalSineCoefficients(coeffs1), canonicalSineCoefficients(coeffs2));
|
|
25628
25764
|
}
|
|
25629
25765
|
});
|
|
25630
25766
|
const Tangent = _.extend({}, PlotDefaults, {
|
|
@@ -27842,212 +27978,6 @@ Graph.defaultProps = {
|
|
|
27842
27978
|
isMobile: false
|
|
27843
27979
|
};
|
|
27844
27980
|
|
|
27845
|
-
/**
|
|
27846
|
-
* A collection of geomtry-related utility functions
|
|
27847
|
-
*/
|
|
27848
|
-
const {
|
|
27849
|
-
eq: eq$1,
|
|
27850
|
-
deepEq: deepEq$2
|
|
27851
|
-
} = Util;
|
|
27852
|
-
|
|
27853
|
-
// This should really be a readonly tuple of [number, number]
|
|
27854
|
-
|
|
27855
|
-
// a, b, c
|
|
27856
|
-
|
|
27857
|
-
function sign(val) {
|
|
27858
|
-
if (eq$1(val, 0)) {
|
|
27859
|
-
return 0;
|
|
27860
|
-
}
|
|
27861
|
-
return val > 0 ? 1 : -1;
|
|
27862
|
-
}
|
|
27863
|
-
function ccw(a, b, c) {
|
|
27864
|
-
return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
|
|
27865
|
-
}
|
|
27866
|
-
function collinear(a, b, c) {
|
|
27867
|
-
return eq$1(ccw(a, b, c), 0);
|
|
27868
|
-
}
|
|
27869
|
-
|
|
27870
|
-
// Given rect bounding points A and B, whether point C is inside the rect
|
|
27871
|
-
function pointInRect(a, b, c) {
|
|
27872
|
-
return c[0] <= Math.max(a[0], b[0]) && c[0] >= Math.min(a[0], b[0]) && c[1] <= Math.max(a[1], b[1]) && c[1] >= Math.min(a[1], b[1]);
|
|
27873
|
-
}
|
|
27874
|
-
|
|
27875
|
-
// Whether line segment AB intersects line segment CD
|
|
27876
|
-
// http://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
|
|
27877
|
-
function intersects(ab, cd) {
|
|
27878
|
-
const triplets = [[ab[0], ab[1], cd[0]], [ab[0], ab[1], cd[1]], [cd[0], cd[1], ab[0]], [cd[0], cd[1], ab[1]]];
|
|
27879
|
-
const orientations = _.map(triplets, function (triplet) {
|
|
27880
|
-
return sign(ccw(...triplet));
|
|
27881
|
-
});
|
|
27882
|
-
if (orientations[0] !== orientations[1] && orientations[2] !== orientations[3]) {
|
|
27883
|
-
return true;
|
|
27884
|
-
}
|
|
27885
|
-
for (let i = 0; i < 4; i++) {
|
|
27886
|
-
if (orientations[i] === 0 && pointInRect(...triplets[i])) {
|
|
27887
|
-
return true;
|
|
27888
|
-
}
|
|
27889
|
-
}
|
|
27890
|
-
return false;
|
|
27891
|
-
}
|
|
27892
|
-
function vector(a, b) {
|
|
27893
|
-
return _.map(_.zip(a, b), function (pair) {
|
|
27894
|
-
return pair[0] - pair[1];
|
|
27895
|
-
});
|
|
27896
|
-
}
|
|
27897
|
-
function magnitude(v) {
|
|
27898
|
-
return Math.sqrt(_.reduce(v, function (memo, el) {
|
|
27899
|
-
// @ts-expect-error - TS2345 - Argument of type 'Coord' is not assignable to parameter of type 'number'.
|
|
27900
|
-
return memo + Math.pow(el, 2);
|
|
27901
|
-
}, 0));
|
|
27902
|
-
}
|
|
27903
|
-
function dotProduct(a, b) {
|
|
27904
|
-
return _.reduce(_.zip(a, b), function (memo, pair) {
|
|
27905
|
-
return memo + pair[0] * pair[1];
|
|
27906
|
-
}, 0);
|
|
27907
|
-
}
|
|
27908
|
-
function sideLengths(coords) {
|
|
27909
|
-
const segments = _.zip(coords, rotate(coords));
|
|
27910
|
-
return segments.map(function (segment) {
|
|
27911
|
-
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
27912
|
-
return magnitude(vector(...segment));
|
|
27913
|
-
});
|
|
27914
|
-
}
|
|
27915
|
-
|
|
27916
|
-
// Based on http://math.stackexchange.com/a/151149
|
|
27917
|
-
function angleMeasures(coords) {
|
|
27918
|
-
const triplets = _.zip(rotate(coords, -1), coords, rotate(coords, 1));
|
|
27919
|
-
const offsets = _.map(triplets, function (triplet) {
|
|
27920
|
-
const p = vector(triplet[1], triplet[0]);
|
|
27921
|
-
const q = vector(triplet[2], triplet[1]);
|
|
27922
|
-
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'Coord'. | TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
27923
|
-
const raw = Math.acos(dotProduct(p, q) / (magnitude(p) * magnitude(q)));
|
|
27924
|
-
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
27925
|
-
return sign(ccw(...triplet)) > 0 ? raw : -raw;
|
|
27926
|
-
});
|
|
27927
|
-
const sum = _.reduce(offsets, function (memo, arg) {
|
|
27928
|
-
return memo + arg;
|
|
27929
|
-
}, 0);
|
|
27930
|
-
return _.map(offsets, function (offset) {
|
|
27931
|
-
return sum > 0 ? Math.PI - offset : Math.PI + offset;
|
|
27932
|
-
});
|
|
27933
|
-
}
|
|
27934
|
-
|
|
27935
|
-
// Whether two polygons are similar (or if specified, congruent)
|
|
27936
|
-
function similar(coords1, coords2, tolerance) {
|
|
27937
|
-
if (coords1.length !== coords2.length) {
|
|
27938
|
-
return false;
|
|
27939
|
-
}
|
|
27940
|
-
const n = coords1.length;
|
|
27941
|
-
const angles1 = angleMeasures(coords1);
|
|
27942
|
-
const angles2 = angleMeasures(coords2);
|
|
27943
|
-
const sides1 = sideLengths(coords1);
|
|
27944
|
-
const sides2 = sideLengths(coords2);
|
|
27945
|
-
for (let i = 0; i < 2 * n; i++) {
|
|
27946
|
-
let angles = angles2.slice();
|
|
27947
|
-
let sides = sides2.slice();
|
|
27948
|
-
|
|
27949
|
-
// Reverse angles and sides to allow matching reflected polygons
|
|
27950
|
-
if (i >= n) {
|
|
27951
|
-
angles.reverse();
|
|
27952
|
-
sides.reverse();
|
|
27953
|
-
// Since sides are calculated from two coordinates,
|
|
27954
|
-
// simply reversing results in an off by one error
|
|
27955
|
-
// @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
27956
|
-
sides = rotate(sides, 1);
|
|
27957
|
-
}
|
|
27958
|
-
|
|
27959
|
-
// @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
27960
|
-
angles = rotate(angles, i);
|
|
27961
|
-
// @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
27962
|
-
sides = rotate(sides, i);
|
|
27963
|
-
if (deepEq$2(angles1, angles)) {
|
|
27964
|
-
const sidePairs = _.zip(sides1, sides);
|
|
27965
|
-
const factors = _.map(sidePairs, function (pair) {
|
|
27966
|
-
return pair[0] / pair[1];
|
|
27967
|
-
});
|
|
27968
|
-
const same = _.all(factors, function (factor) {
|
|
27969
|
-
return eq$1(factors[0], factor);
|
|
27970
|
-
});
|
|
27971
|
-
const congruentEnough = _.all(sidePairs, function (pair) {
|
|
27972
|
-
return number.equal(pair[0], pair[1], tolerance);
|
|
27973
|
-
});
|
|
27974
|
-
if (same && congruentEnough) {
|
|
27975
|
-
return true;
|
|
27976
|
-
}
|
|
27977
|
-
}
|
|
27978
|
-
}
|
|
27979
|
-
return false;
|
|
27980
|
-
}
|
|
27981
|
-
|
|
27982
|
-
// Given triangle with sides ABC return angle opposite side C in degrees
|
|
27983
|
-
function lawOfCosines(a, b, c) {
|
|
27984
|
-
return Math.acos((a * a + b * b - c * c) / (2 * a * b)) * 180 / Math.PI;
|
|
27985
|
-
}
|
|
27986
|
-
function canonicalSineCoefficients([amplitude, angularFrequency, phase, verticalOffset]) {
|
|
27987
|
-
// For a curve of the form f(x) = a * Sin(b * x - c) + d,
|
|
27988
|
-
// this function ensures that a, b > 0, and c is its
|
|
27989
|
-
// smallest possible positive value.
|
|
27990
|
-
|
|
27991
|
-
// Guarantee a > 0
|
|
27992
|
-
if (amplitude < 0) {
|
|
27993
|
-
amplitude *= -1;
|
|
27994
|
-
angularFrequency *= -1;
|
|
27995
|
-
phase *= -1;
|
|
27996
|
-
}
|
|
27997
|
-
const period = 2 * Math.PI;
|
|
27998
|
-
// Guarantee b > 0
|
|
27999
|
-
if (angularFrequency < 0) {
|
|
28000
|
-
angularFrequency *= -1;
|
|
28001
|
-
phase *= -1;
|
|
28002
|
-
phase += period / 2;
|
|
28003
|
-
}
|
|
28004
|
-
|
|
28005
|
-
// Guarantee c is smallest possible positive value
|
|
28006
|
-
while (phase > 0) {
|
|
28007
|
-
phase -= period;
|
|
28008
|
-
}
|
|
28009
|
-
while (phase < 0) {
|
|
28010
|
-
phase += period;
|
|
28011
|
-
}
|
|
28012
|
-
return [amplitude, angularFrequency, phase, verticalOffset];
|
|
28013
|
-
}
|
|
28014
|
-
|
|
28015
|
-
// e.g. rotate([1, 2, 3]) -> [2, 3, 1]
|
|
28016
|
-
function rotate(array, n) {
|
|
28017
|
-
n = typeof n === "undefined" ? 1 : n % array.length;
|
|
28018
|
-
return array.slice(n).concat(array.slice(0, n));
|
|
28019
|
-
}
|
|
28020
|
-
function getLineEquation(first, second) {
|
|
28021
|
-
if (eq$1(first[0], second[0])) {
|
|
28022
|
-
return "x = " + first[0].toFixed(3);
|
|
28023
|
-
}
|
|
28024
|
-
const m = (second[1] - first[1]) / (second[0] - first[0]);
|
|
28025
|
-
const b = first[1] - m * first[0];
|
|
28026
|
-
return "y = " + m.toFixed(3) + "x + " + b.toFixed(3);
|
|
28027
|
-
}
|
|
28028
|
-
|
|
28029
|
-
// Stolen from the wikipedia article
|
|
28030
|
-
// http://en.wikipedia.org/wiki/Line-line_intersection
|
|
28031
|
-
function getLineIntersection(
|
|
28032
|
-
// TODO(LP-10725): update these to be 2-tuples
|
|
28033
|
-
firstPoints, secondPoints) {
|
|
28034
|
-
const x1 = firstPoints[0][0];
|
|
28035
|
-
const y1 = firstPoints[0][1];
|
|
28036
|
-
const x2 = firstPoints[1][0];
|
|
28037
|
-
const y2 = firstPoints[1][1];
|
|
28038
|
-
const x3 = secondPoints[0][0];
|
|
28039
|
-
const y3 = secondPoints[0][1];
|
|
28040
|
-
const x4 = secondPoints[1][0];
|
|
28041
|
-
const y4 = secondPoints[1][1];
|
|
28042
|
-
const determinant = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
|
28043
|
-
if (Math.abs(determinant) < 1e-9) {
|
|
28044
|
-
return "Lines are parallel";
|
|
28045
|
-
}
|
|
28046
|
-
const x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / determinant;
|
|
28047
|
-
const y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / determinant;
|
|
28048
|
-
return "Intersection: (" + x.toFixed(3) + ", " + y.toFixed(3) + ")";
|
|
28049
|
-
}
|
|
28050
|
-
|
|
28051
27981
|
const {
|
|
28052
27982
|
DeprecationMixin
|
|
28053
27983
|
} = Util;
|
|
@@ -29738,8 +29668,8 @@ class InteractiveGraph extends React.Component {
|
|
|
29738
29668
|
const correctCoeffs = this.getSinusoidCoefficients(
|
|
29739
29669
|
// @ts-expect-error - TS2345 - Argument of type 'readonly Coord[] | undefined' is not assignable to parameter of type 'readonly Coord[]'.
|
|
29740
29670
|
rubric.correct.coords);
|
|
29741
|
-
const canonicalGuessCoeffs = canonicalSineCoefficients(guessCoeffs);
|
|
29742
|
-
const canonicalCorrectCoeffs = canonicalSineCoefficients(correctCoeffs);
|
|
29671
|
+
const canonicalGuessCoeffs = canonicalSineCoefficients$1(guessCoeffs);
|
|
29672
|
+
const canonicalCorrectCoeffs = canonicalSineCoefficients$1(correctCoeffs);
|
|
29743
29673
|
// If the canonical coefficients match, it's correct.
|
|
29744
29674
|
if (deepEq$1(canonicalGuessCoeffs, canonicalCorrectCoeffs)) {
|
|
29745
29675
|
return {
|
|
@@ -31468,7 +31398,6 @@ class LabelImage extends React.Component {
|
|
|
31468
31398
|
|
|
31469
31399
|
// Update Perseus widget state with user selected answers without
|
|
31470
31400
|
// triggering interaction events for listeners.
|
|
31471
|
-
// @ts-expect-error - TS2345 - Argument of type 'null' is not assignable to parameter of type '(() => unknown) | undefined'.
|
|
31472
31401
|
onChange({
|
|
31473
31402
|
markers: updatedMarkers
|
|
31474
31403
|
}, null, true);
|
|
@@ -41572,7 +41501,7 @@ const init = function init(options) {
|
|
|
41572
41501
|
|
|
41573
41502
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
41574
41503
|
const libName = "@khanacademy/perseus";
|
|
41575
|
-
const libVersion = "19.
|
|
41504
|
+
const libVersion = "19.1.1";
|
|
41576
41505
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
41577
41506
|
|
|
41578
41507
|
const apiVersion = {
|
|
@@ -44559,15 +44488,15 @@ function injectWidgets(content, widgets) {
|
|
|
44559
44488
|
}
|
|
44560
44489
|
|
|
44561
44490
|
/**
|
|
44562
|
-
* Helper to parse
|
|
44491
|
+
* Helper to parse PerseusItem JSON
|
|
44563
44492
|
* Why not just use JSON.parse? We want:
|
|
44564
44493
|
* - To make sure types are correct
|
|
44565
44494
|
* - To give us a central place to validate/transform output if needed
|
|
44566
|
-
* @param {string}
|
|
44495
|
+
* @param {string} json - the stringified PerseusItem JSON
|
|
44567
44496
|
* @returns {PerseusItem} the parsed PerseusItem object
|
|
44568
44497
|
*/
|
|
44569
|
-
function
|
|
44570
|
-
return JSON.parse(
|
|
44498
|
+
function parsePerseusItem(json) {
|
|
44499
|
+
return JSON.parse(json);
|
|
44571
44500
|
}
|
|
44572
44501
|
|
|
44573
44502
|
const EditorJsonify = {
|
|
@@ -44578,5 +44507,5 @@ const EditorJsonify = {
|
|
|
44578
44507
|
}
|
|
44579
44508
|
};
|
|
44580
44509
|
|
|
44581
|
-
export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, displaySigFigs, getAnswersFromWidgets, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, mediaQueries,
|
|
44510
|
+
export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, displaySigFigs, getAnswersFromWidgets, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, traverse, violatingWidgets, allWidgets as widgets };
|
|
44582
44511
|
//# sourceMappingURL=index.js.map
|