@khanacademy/perseus 24.2.0 → 25.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/index.css +7 -0
- package/dist/es/index.js +845 -377
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +7 -0
- package/dist/index.d.ts +3 -4
- package/dist/index.js +927 -449
- package/dist/index.js.map +1 -1
- package/dist/logging/log.d.ts +1 -41
- package/dist/perseus-api.d.ts +1 -0
- package/dist/perseus-types.d.ts +17 -2
- package/dist/server-item-renderer.d.ts +2 -1
- package/dist/types.d.ts +1 -1
- package/dist/widgets/expression.d.ts +8 -2
- package/dist/widgets/interactive-graph.d.ts +1 -1
- package/dist/widgets/interactive-graphs/graphs/angle.d.ts +5 -0
- package/dist/widgets/interactive-graphs/graphs/components/{angle.d.ts → angle-indicators.d.ts} +12 -2
- package/dist/widgets/interactive-graphs/graphs/components/movable-line.d.ts +11 -0
- package/dist/widgets/interactive-graphs/graphs/components/text-label.d.ts +1 -1
- package/dist/widgets/interactive-graphs/graphs/index.d.ts +1 -0
- package/dist/widgets/interactive-graphs/graphs/utils.d.ts +0 -3
- package/dist/widgets/interactive-graphs/index.d.ts +1 -1
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +3 -1
- package/dist/widgets/interactive-graphs/locked-figures/locked-function.d.ts +4 -0
- package/dist/widgets/interactive-graphs/mafs-graph.d.ts +0 -19
- package/dist/widgets/interactive-graphs/mafs-supported-graph-types.d.ts +1 -1
- package/dist/widgets/interactive-graphs/math/angles.d.ts +5 -0
- package/dist/widgets/interactive-graphs/math/box.d.ts +4 -0
- package/dist/widgets/interactive-graphs/math/clamp.d.ts +1 -0
- package/dist/widgets/interactive-graphs/math/coordinates.d.ts +2 -0
- package/dist/widgets/interactive-graphs/math/geometry.d.ts +3 -0
- package/dist/widgets/interactive-graphs/math/index.d.ts +8 -0
- package/dist/widgets/interactive-graphs/math/interpolation.d.ts +1 -0
- package/dist/widgets/interactive-graphs/math/interval.d.ts +5 -0
- package/dist/widgets/interactive-graphs/math/snap.d.ts +2 -0
- package/dist/widgets/interactive-graphs/stateful-mafs-graph.d.ts +21 -0
- package/dist/widgets/interactive-graphs/types.d.ts +9 -1
- package/dist/widgets/interactive-graphs/utils.d.ts +0 -2
- package/dist/widgets/label-image.d.ts +2 -0
- package/package.json +8 -8
- package/dist/perseus-error.d.ts +0 -11
package/dist/es/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as KAS from '@khanacademy/kas';
|
|
2
2
|
import { MathInputI18nContext, CursorContext, getKeyTranslator, createMathField, getCursorContext, DesktopKeypad, convertDotToTimesByLocale, mathQuillInstance, KeypadInput, KeypadType, KeyArray, keypadElementPropType } from '@khanacademy/math-input';
|
|
3
|
+
import { PerseusError, Errors, addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
|
|
3
4
|
import * as PerseusLinter from '@khanacademy/perseus-linter';
|
|
4
5
|
import { linterContextDefault, Rule, linterContextProps } from '@khanacademy/perseus-linter';
|
|
5
6
|
import { View, RenderStateRoot, UniqueIDProvider, useLatestRef, useUniqueIdWithMock, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
|
|
@@ -29,7 +30,7 @@ import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-block
|
|
|
29
30
|
import caretDown from '@phosphor-icons/core/regular/caret-down.svg';
|
|
30
31
|
import caretUp from '@phosphor-icons/core/regular/caret-up.svg';
|
|
31
32
|
import debounce$2 from 'lodash.debounce';
|
|
32
|
-
import { vec, Ellipse, Line as Line$4, Point as Point$2, Polygon, useTransformContext, Text as Text$1,
|
|
33
|
+
import { vec, Ellipse, Plot as Plot$3, Line as Line$4, Point as Point$2, Polygon, useTransformContext, Text as Text$1, Coordinates, Mafs } from 'mafs';
|
|
33
34
|
import { useDrag } from '@use-gesture/react';
|
|
34
35
|
import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
|
|
35
36
|
import { Body, LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
|
|
@@ -39,7 +40,6 @@ import Switch from '@khanacademy/wonder-blocks-switch';
|
|
|
39
40
|
import Pill from '@khanacademy/wonder-blocks-pill';
|
|
40
41
|
import { Popper } from 'react-popper';
|
|
41
42
|
import Link from '@khanacademy/wonder-blocks-link';
|
|
42
|
-
import { addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
|
|
43
43
|
|
|
44
44
|
function _extends() {
|
|
45
45
|
_extends = Object.assign ? Object.assign.bind() : function (target) {
|
|
@@ -699,52 +699,9 @@ var dependencies = /*#__PURE__*/Object.freeze({
|
|
|
699
699
|
// TODO(LP-11481): Change the primary API we use for logging in Perseus to use
|
|
700
700
|
// getDependencies() internally... simplify usage to just using a "singleton"
|
|
701
701
|
// which get's the injected logger.
|
|
702
|
-
/**
|
|
703
|
-
* @typedef {Object} Errors utility for referencing the Perseus error taxonomy.
|
|
704
|
-
*/
|
|
705
|
-
const Errors = Object.freeze({
|
|
706
|
-
/**
|
|
707
|
-
* @property {ErrorKind} Unknown The kind of error is not known.
|
|
708
|
-
*/
|
|
709
|
-
Unknown: "Unknown",
|
|
710
|
-
/**
|
|
711
|
-
* @property {ErrorKind} Internal The error is internal to the executing code.
|
|
712
|
-
*/
|
|
713
|
-
Internal: "Internal",
|
|
714
|
-
/**
|
|
715
|
-
* @property {ErrorKind} InvalidInput There was a problem with the provided
|
|
716
|
-
* input, such as the wrong format or a null value.
|
|
717
|
-
*/
|
|
718
|
-
InvalidInput: "InvalidInput",
|
|
719
|
-
/**
|
|
720
|
-
* @property {ErrorKind} NotAllowed There was a problem due to the state of
|
|
721
|
-
* the system not matching the requested operation or input. For example,
|
|
722
|
-
* trying to create a username that is valid, but is already taken by
|
|
723
|
-
* another user. Use {@link InvalidInput} instead when the input isn't
|
|
724
|
-
* valid regardless of the state of the system. Use {@link NotFound} when
|
|
725
|
-
* the failure is due to not being able to find a resource.
|
|
726
|
-
*/
|
|
727
|
-
NotAllowed: "NotAllowed",
|
|
728
|
-
/**
|
|
729
|
-
* @property {ErrorKind} TransientService There was a problem when making a
|
|
730
|
-
* request to a service.
|
|
731
|
-
*/
|
|
732
|
-
TransientService: "TransientService",
|
|
733
|
-
/**
|
|
734
|
-
* @property {ErrorKind} Service There was a non-transient problem when
|
|
735
|
-
* making a request to service.
|
|
736
|
-
*/
|
|
737
|
-
Service: "Service"
|
|
738
|
-
});
|
|
739
|
-
|
|
740
|
-
/**
|
|
741
|
-
* @type {ErrorKind} The kind of error being reported
|
|
742
|
-
*/
|
|
743
|
-
|
|
744
702
|
/**
|
|
745
703
|
* Provides logging infrastructure for Perseus
|
|
746
704
|
*/
|
|
747
|
-
|
|
748
705
|
const Log = {
|
|
749
706
|
// Logs a message
|
|
750
707
|
log: (message, extra) => {
|
|
@@ -756,16 +713,6 @@ const Log = {
|
|
|
756
713
|
}
|
|
757
714
|
};
|
|
758
715
|
|
|
759
|
-
class PerseusError extends Error {
|
|
760
|
-
constructor(message, kind, options) {
|
|
761
|
-
super(message);
|
|
762
|
-
this.kind = void 0;
|
|
763
|
-
this.metadata = void 0;
|
|
764
|
-
this.kind = kind;
|
|
765
|
-
this.metadata = options == null ? void 0 : options.metadata;
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
|
|
769
716
|
/**
|
|
770
717
|
* These are things that widgets should exclude when serializing themselves.
|
|
771
718
|
*
|
|
@@ -8969,7 +8916,7 @@ var Tex$1 = {
|
|
|
8969
8916
|
const {
|
|
8970
8917
|
processMath: processMath$1
|
|
8971
8918
|
} = Tex$1;
|
|
8972
|
-
function polar(r, th) {
|
|
8919
|
+
function polar$1(r, th) {
|
|
8973
8920
|
if (typeof r === "number") {
|
|
8974
8921
|
r = [r, r];
|
|
8975
8922
|
}
|
|
@@ -9458,8 +9405,8 @@ class Graphie$1 {
|
|
|
9458
9405
|
endAngle = (endAngle % 360 + 360) % 360;
|
|
9459
9406
|
const cent = this.scalePoint(center);
|
|
9460
9407
|
const radii = this.scaleVector(radius);
|
|
9461
|
-
const startVector = polar(radius, startAngle);
|
|
9462
|
-
const endVector = polar(radius, endAngle);
|
|
9408
|
+
const startVector = polar$1(radius, startAngle);
|
|
9409
|
+
const endVector = polar$1(radius, endAngle);
|
|
9463
9410
|
|
|
9464
9411
|
// We round the coordinates to make testing easier, because trig
|
|
9465
9412
|
// operations return unround numbers.
|
|
@@ -13732,7 +13679,7 @@ _.extend(MovableAngle.prototype, {
|
|
|
13732
13679
|
let angle = GraphUtils.findAngle(newPoint, vertex);
|
|
13733
13680
|
angle = Math.round((angle - snapOffset) / snap) * snap + snapOffset;
|
|
13734
13681
|
const distance = GraphUtils.getDistance(newPoint, vertex);
|
|
13735
|
-
return vector$1.add(vertex, polar(distance, angle));
|
|
13682
|
+
return vector$1.add(vertex, polar$1(distance, angle));
|
|
13736
13683
|
}
|
|
13737
13684
|
return true;
|
|
13738
13685
|
};
|
|
@@ -16648,7 +16595,7 @@ const Path = GraphieClasses.createClass({
|
|
|
16648
16595
|
this.path.toFront();
|
|
16649
16596
|
}
|
|
16650
16597
|
});
|
|
16651
|
-
const Arc = GraphieClasses.createSimpleClass((graphie, props) => {
|
|
16598
|
+
const Arc$1 = GraphieClasses.createSimpleClass((graphie, props) => {
|
|
16652
16599
|
let center = props.center;
|
|
16653
16600
|
let radius = props.radius;
|
|
16654
16601
|
if (props.unscaled) {
|
|
@@ -16664,7 +16611,7 @@ const Rect$1 = GraphieClasses.createSimpleClass((graphie, props) => {
|
|
|
16664
16611
|
return graphie.rect(props.x, props.y, props.width, props.height, props.style);
|
|
16665
16612
|
});
|
|
16666
16613
|
var Movables = {
|
|
16667
|
-
Arc: Arc,
|
|
16614
|
+
Arc: Arc$1,
|
|
16668
16615
|
Circle: Circle,
|
|
16669
16616
|
Label: Label$1,
|
|
16670
16617
|
Line: Line$3,
|
|
@@ -28363,6 +28310,125 @@ const useTransformDimensionsToPixels = (...dimens) => {
|
|
|
28363
28310
|
return dimensionsToPixels(dimens, graphState);
|
|
28364
28311
|
};
|
|
28365
28312
|
|
|
28313
|
+
// Restricts `value` to be between `min` and `max`.
|
|
28314
|
+
function clamp(value, min, max) {
|
|
28315
|
+
if (value < min) {
|
|
28316
|
+
return min;
|
|
28317
|
+
}
|
|
28318
|
+
if (value > max) {
|
|
28319
|
+
return max;
|
|
28320
|
+
}
|
|
28321
|
+
return value;
|
|
28322
|
+
}
|
|
28323
|
+
|
|
28324
|
+
// Rounds each component of `point` to the nearest multiple of the
|
|
28325
|
+
// corresponding component of `snapStep`. E.g. if the snap step is [2, 3],
|
|
28326
|
+
// the x-coord of the point will be rounded to the nearest multiple of 2, and
|
|
28327
|
+
// the y-coord of the point will be rounded to the nearest multiple of 3.
|
|
28328
|
+
function snap(snapStep, point) {
|
|
28329
|
+
const [requestedX, requestedY] = point;
|
|
28330
|
+
const [snapX, snapY] = snapStep;
|
|
28331
|
+
return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
|
|
28332
|
+
}
|
|
28333
|
+
|
|
28334
|
+
// Use X and Y to index into arrays that represent coordinate pairs.
|
|
28335
|
+
// e.g. point[X]
|
|
28336
|
+
const X = 0;
|
|
28337
|
+
const Y = 1;
|
|
28338
|
+
|
|
28339
|
+
// Use MIN and MAX to index into arrays that represent intervals.
|
|
28340
|
+
// e.g. range[MAX]
|
|
28341
|
+
const MIN = 0;
|
|
28342
|
+
const MAX = 1;
|
|
28343
|
+
function size([min, max]) {
|
|
28344
|
+
return max - min;
|
|
28345
|
+
}
|
|
28346
|
+
|
|
28347
|
+
// Removes an equal amount from each end of the interval.
|
|
28348
|
+
//
|
|
28349
|
+
// If the given amount cannot be removed (because the interval is too small),
|
|
28350
|
+
// half the interval's size is removed from each end instead, so the interval
|
|
28351
|
+
// ends up with size = 0, and both MAX and MIN equal to its former midpoint.
|
|
28352
|
+
//
|
|
28353
|
+
// If `amount` is negative, the interval is expanded instead.
|
|
28354
|
+
function trim(amount, interval) {
|
|
28355
|
+
if (amount * 2 > size(interval)) {
|
|
28356
|
+
const middle = average(...interval);
|
|
28357
|
+
return [middle, middle];
|
|
28358
|
+
}
|
|
28359
|
+
return [interval[MIN] + amount, interval[MAX] - amount];
|
|
28360
|
+
}
|
|
28361
|
+
function average(a, b) {
|
|
28362
|
+
return (a + b) / 2;
|
|
28363
|
+
}
|
|
28364
|
+
|
|
28365
|
+
// Restricts the `point` to be within `box`, by clamping each coordinate to the
|
|
28366
|
+
// corresponding interval.
|
|
28367
|
+
function clampToBox(box, point) {
|
|
28368
|
+
return [clamp(point[X], ...box[X]), clamp(point[Y], ...box[Y])];
|
|
28369
|
+
}
|
|
28370
|
+
|
|
28371
|
+
// Reduces the size of `box`, trimming each corner by the given positive
|
|
28372
|
+
// `amount`. If a component of `amount` is negative, the box is expanded in
|
|
28373
|
+
// that dimension instead.
|
|
28374
|
+
function inset(amount, box) {
|
|
28375
|
+
return [trim(amount[X], box[X]), trim(amount[Y], box[Y])];
|
|
28376
|
+
}
|
|
28377
|
+
|
|
28378
|
+
// [L]inear Int[erp]olation: gets the weighted average of two values `a` and
|
|
28379
|
+
// `b`, with the given `fraction` specifying how much weight `a` and `b` get:
|
|
28380
|
+
// - if `fraction` is 0, `lerp` returns `a`.
|
|
28381
|
+
// - if `fraction` is 0.5, `lerp` returns the average of `a` and `b`.
|
|
28382
|
+
// - if `fraction` is 1, `lerp` returns `b`.
|
|
28383
|
+
function lerp(a, b, fraction) {
|
|
28384
|
+
return (b - a) * clamp(fraction, 0, 1) + a;
|
|
28385
|
+
}
|
|
28386
|
+
|
|
28387
|
+
// Determines whether two line segments have exactly one intersection point.
|
|
28388
|
+
// The segments are treated as "open" - that is, not inclusive of their
|
|
28389
|
+
// endpoints.
|
|
28390
|
+
// Algorithm from https://stackoverflow.com/a/24392281/7347484
|
|
28391
|
+
const segmentsIntersect = ([[a, b], [c, d]], [[p, q], [r, s]]) => {
|
|
28392
|
+
const determinant = (c - a) * (s - q) - (r - p) * (d - b);
|
|
28393
|
+
if (determinant === 0) {
|
|
28394
|
+
return false;
|
|
28395
|
+
} else {
|
|
28396
|
+
const lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / determinant;
|
|
28397
|
+
const gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / determinant;
|
|
28398
|
+
return 0 < lambda && lambda < 1 && 0 < gamma && gamma < 1;
|
|
28399
|
+
}
|
|
28400
|
+
};
|
|
28401
|
+
|
|
28402
|
+
// This file contains helper functions for working with angles.
|
|
28403
|
+
|
|
28404
|
+
function convertDegreesToRadians(degrees) {
|
|
28405
|
+
return degrees / 180 * Math.PI;
|
|
28406
|
+
}
|
|
28407
|
+
|
|
28408
|
+
// Returns a value between -180 and 180, inclusive. The angle is measured
|
|
28409
|
+
// between the positive x-axis and the given vector.
|
|
28410
|
+
function calculateAngleInDegrees([x, y]) {
|
|
28411
|
+
return Math.atan2(y, x) * 180 / Math.PI;
|
|
28412
|
+
}
|
|
28413
|
+
function polar(r, th) {
|
|
28414
|
+
if (typeof r === "number") {
|
|
28415
|
+
r = [r, r];
|
|
28416
|
+
}
|
|
28417
|
+
th = th * Math.PI / 180;
|
|
28418
|
+
return [r[0] * Math.cos(th), r[1] * Math.sin(th)];
|
|
28419
|
+
}
|
|
28420
|
+
const findAngle = (point1, point2, vertex) => {
|
|
28421
|
+
if (vertex === undefined) {
|
|
28422
|
+
const x = point1[0] - point2[0];
|
|
28423
|
+
const y = point1[1] - point2[1];
|
|
28424
|
+
if (!x && !y) {
|
|
28425
|
+
return 0;
|
|
28426
|
+
}
|
|
28427
|
+
return (180 + Math.atan2(-y, -x) * 180 / Math.PI + 360) % 360;
|
|
28428
|
+
}
|
|
28429
|
+
return findAngle(point1, vertex) - findAngle(point2, vertex);
|
|
28430
|
+
};
|
|
28431
|
+
|
|
28366
28432
|
function AxisLabels() {
|
|
28367
28433
|
const {
|
|
28368
28434
|
range,
|
|
@@ -28370,8 +28436,8 @@ function AxisLabels() {
|
|
|
28370
28436
|
width,
|
|
28371
28437
|
height
|
|
28372
28438
|
} = useGraphConfig();
|
|
28373
|
-
const yAxisLabelLocation = [0, range[
|
|
28374
|
-
const xAxisLabelLocation = [range[
|
|
28439
|
+
const yAxisLabelLocation = [0, range[Y][MAX]];
|
|
28440
|
+
const xAxisLabelLocation = [range[X][MAX], 0];
|
|
28375
28441
|
const [xAxisLabelText, yAxisLabelText] = labels;
|
|
28376
28442
|
const graphInfo = {
|
|
28377
28443
|
range,
|
|
@@ -28542,6 +28608,32 @@ const LockedEllipse = props => {
|
|
|
28542
28608
|
});
|
|
28543
28609
|
};
|
|
28544
28610
|
|
|
28611
|
+
const LockedFunction = props => {
|
|
28612
|
+
const {
|
|
28613
|
+
color,
|
|
28614
|
+
strokeStyle,
|
|
28615
|
+
directionalAxis,
|
|
28616
|
+
domain
|
|
28617
|
+
} = props;
|
|
28618
|
+
const plotProps = {
|
|
28619
|
+
color: lockedFigureColors[color],
|
|
28620
|
+
style: strokeStyle,
|
|
28621
|
+
domain
|
|
28622
|
+
};
|
|
28623
|
+
const equation = props.equationParsed || KAS.parse(props.equation).expr;
|
|
28624
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
28625
|
+
className: "locked-function"
|
|
28626
|
+
}, directionalAxis === "x" && /*#__PURE__*/React.createElement(Plot$3.OfX, _extends({
|
|
28627
|
+
y: x => equation.eval({
|
|
28628
|
+
x
|
|
28629
|
+
})
|
|
28630
|
+
}, plotProps)), directionalAxis === "y" && /*#__PURE__*/React.createElement(Plot$3.OfY, _extends({
|
|
28631
|
+
x: y => equation.eval({
|
|
28632
|
+
y
|
|
28633
|
+
})
|
|
28634
|
+
}, plotProps)));
|
|
28635
|
+
};
|
|
28636
|
+
|
|
28545
28637
|
function pathBuilder() {
|
|
28546
28638
|
return new PathBuilder();
|
|
28547
28639
|
}
|
|
@@ -28640,7 +28732,7 @@ function Arrowhead(props) {
|
|
|
28640
28732
|
const [point] = useTransformVectorsToPixels(props.tip);
|
|
28641
28733
|
return /*#__PURE__*/React.createElement("g", {
|
|
28642
28734
|
className: "interactive-graph-arrowhead",
|
|
28643
|
-
transform: `translate(${point[
|
|
28735
|
+
transform: `translate(${point[X]} ${point[Y]}) rotate(${props.angle})`
|
|
28644
28736
|
}, /*#__PURE__*/React.createElement("g", {
|
|
28645
28737
|
transform: "translate(-1.5)"
|
|
28646
28738
|
}, /*#__PURE__*/React.createElement("path", {
|
|
@@ -28655,51 +28747,6 @@ function Arrowhead(props) {
|
|
|
28655
28747
|
})));
|
|
28656
28748
|
}
|
|
28657
28749
|
|
|
28658
|
-
function calculateAngleInDegrees([x, y]) {
|
|
28659
|
-
return Math.atan2(y, x) * 180 / Math.PI;
|
|
28660
|
-
}
|
|
28661
|
-
|
|
28662
|
-
/**
|
|
28663
|
-
* Given a ray and a rectangular box, find the point where the ray intersects
|
|
28664
|
-
* the edge of the box. Assumes the `initialPoint` is inside the box.
|
|
28665
|
-
* @param initialPoint - The starting point of the ray.
|
|
28666
|
-
* @param throughPoint - A point that the ray passes through. Must be different from initialPoint.
|
|
28667
|
-
* @param box - The box with which to intersect the ray, in the form [[xMin, xMax], [yMin, yMax]]
|
|
28668
|
-
*/
|
|
28669
|
-
const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
|
|
28670
|
-
const [[xMin, xMax], [yMin, yMax]] = box;
|
|
28671
|
-
const [aX, aY] = initialPoint;
|
|
28672
|
-
const [bX, bY] = throughPoint;
|
|
28673
|
-
const yDiff = bY - aY;
|
|
28674
|
-
const xDiff = bX - aX;
|
|
28675
|
-
const slope = yDiff / xDiff;
|
|
28676
|
-
const inverseSlope = 1 / slope;
|
|
28677
|
-
const xExtreme = xDiff < 0 ? xMin : xMax;
|
|
28678
|
-
const yExtreme = yDiff < 0 ? yMin : yMax;
|
|
28679
|
-
const yAtXExtreme = aY + (xExtreme - aX) * slope;
|
|
28680
|
-
const xAtYExtreme = aX + (yExtreme - aY) * inverseSlope;
|
|
28681
|
-
switch (true) {
|
|
28682
|
-
// does the ray exit the graph bounding box via the left or right edge?
|
|
28683
|
-
case isBetween(yAtXExtreme, yMin, yMax):
|
|
28684
|
-
return [xExtreme, yAtXExtreme];
|
|
28685
|
-
|
|
28686
|
-
// does the ray exit the graph bounding box via the top or bottom edge?
|
|
28687
|
-
case isBetween(xAtYExtreme, xMin, xMax):
|
|
28688
|
-
return [xAtYExtreme, yExtreme];
|
|
28689
|
-
default:
|
|
28690
|
-
return [xExtreme, yExtreme];
|
|
28691
|
-
}
|
|
28692
|
-
};
|
|
28693
|
-
const getLines = points => {
|
|
28694
|
-
return points.map((point, i) => {
|
|
28695
|
-
const next = points[(i + 1) % points.length];
|
|
28696
|
-
return [point, next];
|
|
28697
|
-
});
|
|
28698
|
-
};
|
|
28699
|
-
function isBetween(x, low, high) {
|
|
28700
|
-
return x >= low && x <= high;
|
|
28701
|
-
}
|
|
28702
|
-
|
|
28703
28750
|
function SVGLine(props) {
|
|
28704
28751
|
const {
|
|
28705
28752
|
start,
|
|
@@ -28709,10 +28756,10 @@ function SVGLine(props) {
|
|
|
28709
28756
|
testId
|
|
28710
28757
|
} = props;
|
|
28711
28758
|
return /*#__PURE__*/React.createElement("line", {
|
|
28712
|
-
x1: start[
|
|
28713
|
-
y1: start[
|
|
28714
|
-
x2: end[
|
|
28715
|
-
y2: end[
|
|
28759
|
+
x1: start[X],
|
|
28760
|
+
y1: start[Y],
|
|
28761
|
+
x2: end[X],
|
|
28762
|
+
y2: end[Y],
|
|
28716
28763
|
style: style,
|
|
28717
28764
|
className: className,
|
|
28718
28765
|
"data-testid": testId
|
|
@@ -28745,6 +28792,41 @@ function Vector(props) {
|
|
|
28745
28792
|
}));
|
|
28746
28793
|
}
|
|
28747
28794
|
|
|
28795
|
+
/**
|
|
28796
|
+
* Given a ray and a rectangular box, find the point where the ray intersects
|
|
28797
|
+
* the edge of the box. Assumes the `initialPoint` is inside the box.
|
|
28798
|
+
* @param initialPoint - The starting point of the ray.
|
|
28799
|
+
* @param throughPoint - A point that the ray passes through. Must be different from initialPoint.
|
|
28800
|
+
* @param box - The box with which to intersect the ray, in the form [[xMin, xMax], [yMin, yMax]]
|
|
28801
|
+
*/
|
|
28802
|
+
const getIntersectionOfRayWithBox = (initialPoint, throughPoint, box) => {
|
|
28803
|
+
const [[xMin, xMax], [yMin, yMax]] = box;
|
|
28804
|
+
const [aX, aY] = initialPoint;
|
|
28805
|
+
const [bX, bY] = throughPoint;
|
|
28806
|
+
const yDiff = bY - aY;
|
|
28807
|
+
const xDiff = bX - aX;
|
|
28808
|
+
const slope = yDiff / xDiff;
|
|
28809
|
+
const inverseSlope = 1 / slope;
|
|
28810
|
+
const xExtreme = xDiff < 0 ? xMin : xMax;
|
|
28811
|
+
const yExtreme = yDiff < 0 ? yMin : yMax;
|
|
28812
|
+
const yAtXExtreme = aY + (xExtreme - aX) * slope;
|
|
28813
|
+
const xAtYExtreme = aX + (yExtreme - aY) * inverseSlope;
|
|
28814
|
+
switch (true) {
|
|
28815
|
+
// does the ray exit the graph bounding box via the left or right edge?
|
|
28816
|
+
case isBetween(yAtXExtreme, yMin, yMax):
|
|
28817
|
+
return [xExtreme, yAtXExtreme];
|
|
28818
|
+
|
|
28819
|
+
// does the ray exit the graph bounding box via the top or bottom edge?
|
|
28820
|
+
case isBetween(xAtYExtreme, xMin, xMax):
|
|
28821
|
+
return [xAtYExtreme, yExtreme];
|
|
28822
|
+
default:
|
|
28823
|
+
return [xExtreme, yExtreme];
|
|
28824
|
+
}
|
|
28825
|
+
};
|
|
28826
|
+
function isBetween(x, low, high) {
|
|
28827
|
+
return x >= low && x <= high;
|
|
28828
|
+
}
|
|
28829
|
+
|
|
28748
28830
|
const LockedLine = props => {
|
|
28749
28831
|
const {
|
|
28750
28832
|
color: color$1,
|
|
@@ -28797,8 +28879,8 @@ const LockedLine = props => {
|
|
|
28797
28879
|
return /*#__PURE__*/React.createElement("g", {
|
|
28798
28880
|
className: kind === "ray" ? "locked-ray" : "locked-line"
|
|
28799
28881
|
}, line, showPoint1 && /*#__PURE__*/React.createElement(Point$2, {
|
|
28800
|
-
x: point1.coord[
|
|
28801
|
-
y: point1.coord[
|
|
28882
|
+
x: point1.coord[X],
|
|
28883
|
+
y: point1.coord[Y],
|
|
28802
28884
|
svgCircleProps: {
|
|
28803
28885
|
style: {
|
|
28804
28886
|
fill: point1.filled ? lockedFigureColors[point1.color] : color.white,
|
|
@@ -28807,8 +28889,8 @@ const LockedLine = props => {
|
|
|
28807
28889
|
}
|
|
28808
28890
|
}
|
|
28809
28891
|
}), showPoint2 && /*#__PURE__*/React.createElement(Point$2, {
|
|
28810
|
-
x: point2.coord[
|
|
28811
|
-
y: point2.coord[
|
|
28892
|
+
x: point2.coord[X],
|
|
28893
|
+
y: point2.coord[Y],
|
|
28812
28894
|
svgCircleProps: {
|
|
28813
28895
|
style: {
|
|
28814
28896
|
fill: point2.filled ? lockedFigureColors[point2.color] : color.white,
|
|
@@ -28856,8 +28938,8 @@ const LockedPolygon = props => {
|
|
|
28856
28938
|
color: lockedFigureColors[color]
|
|
28857
28939
|
}), showVertices && points.map((point, index) => /*#__PURE__*/React.createElement(Point$2, {
|
|
28858
28940
|
key: `locked-polygon-point-${index}`,
|
|
28859
|
-
x: point[
|
|
28860
|
-
y: point[
|
|
28941
|
+
x: point[X],
|
|
28942
|
+
y: point[Y],
|
|
28861
28943
|
color: lockedFigureColors[color]
|
|
28862
28944
|
})));
|
|
28863
28945
|
};
|
|
@@ -28904,6 +28986,10 @@ const GraphLockedLayer = props => {
|
|
|
28904
28986
|
return /*#__PURE__*/React.createElement(LockedPolygon, _extends({
|
|
28905
28987
|
key: `polygon-${index}`
|
|
28906
28988
|
}, figure));
|
|
28989
|
+
case "function":
|
|
28990
|
+
return /*#__PURE__*/React.createElement(LockedFunction, _extends({
|
|
28991
|
+
key: `function-${index}`
|
|
28992
|
+
}, figure));
|
|
28907
28993
|
default:
|
|
28908
28994
|
/**
|
|
28909
28995
|
* Devlopment-time future-proofing: This should
|
|
@@ -28996,24 +29082,18 @@ const normalizePoints = (range, step, coordsList, noSnap) => coordsList.map(coor
|
|
|
28996
29082
|
coords.map((coord, i) => {
|
|
28997
29083
|
const axisRange = range[i];
|
|
28998
29084
|
if (noSnap) {
|
|
28999
|
-
return axisRange[
|
|
29085
|
+
return axisRange[MIN] + size(axisRange) * coord;
|
|
29000
29086
|
}
|
|
29001
29087
|
const axisStep = step[i];
|
|
29002
|
-
const nSteps = Math.floor((axisRange
|
|
29088
|
+
const nSteps = Math.floor(size(axisRange) / axisStep);
|
|
29003
29089
|
const tick = Math.round(coord * nSteps);
|
|
29004
|
-
return axisRange[
|
|
29090
|
+
return axisRange[MIN] + axisStep * tick;
|
|
29005
29091
|
}));
|
|
29006
29092
|
|
|
29007
29093
|
// same as normalizeCoords in interactive-graph.tsx
|
|
29008
29094
|
const normalizeCoords = (coordsList, ranges) => coordsList.map(coords => coords.map((coord, i) => {
|
|
29009
|
-
|
|
29010
|
-
return (coord + ranges[i][1]) / extent;
|
|
29095
|
+
return (coord + ranges[i][1]) / size(ranges[i]);
|
|
29011
29096
|
}));
|
|
29012
|
-
function snap(snapStep, point) {
|
|
29013
|
-
const [requestedX, requestedY] = point;
|
|
29014
|
-
const [snapX, snapY] = snapStep;
|
|
29015
|
-
return [Math.round(requestedX / snapX) * snapX, Math.round(requestedY / snapY) * snapY];
|
|
29016
|
-
}
|
|
29017
29097
|
|
|
29018
29098
|
// Returns the closest point to the given `point` that is within the graph
|
|
29019
29099
|
// bounds given in `state`.
|
|
@@ -29022,19 +29102,8 @@ function bound$1({
|
|
|
29022
29102
|
range,
|
|
29023
29103
|
point
|
|
29024
29104
|
}) {
|
|
29025
|
-
const
|
|
29026
|
-
|
|
29027
|
-
const [[minX, maxX], [minY, maxY]] = range;
|
|
29028
|
-
return [clamp(requestedX, minX + snapX, maxX - snapX), clamp(requestedY, minY + snapY, maxY - snapY)];
|
|
29029
|
-
}
|
|
29030
|
-
function clamp(value, min, max) {
|
|
29031
|
-
if (value < min) {
|
|
29032
|
-
return min;
|
|
29033
|
-
}
|
|
29034
|
-
if (value > max) {
|
|
29035
|
-
return max;
|
|
29036
|
-
}
|
|
29037
|
-
return value;
|
|
29105
|
+
const boundingBox = inset(snapStep, range);
|
|
29106
|
+
return clampToBox(boundingBox, point);
|
|
29038
29107
|
}
|
|
29039
29108
|
|
|
29040
29109
|
var isProduction = process.env.NODE_ENV === 'production';
|
|
@@ -29104,8 +29173,8 @@ function useDraggable(args) {
|
|
|
29104
29173
|
metaKey,
|
|
29105
29174
|
shiftKey
|
|
29106
29175
|
} = state;
|
|
29107
|
-
const direction = [yDownDirection[
|
|
29108
|
-
const span = Math.abs(direction[
|
|
29176
|
+
const direction = [yDownDirection[X], -yDownDirection[Y]];
|
|
29177
|
+
const span = Math.abs(direction[X]) ? xSpan : ySpan;
|
|
29109
29178
|
let divisions = 50;
|
|
29110
29179
|
if (altKey || metaKey) {
|
|
29111
29180
|
divisions = 200;
|
|
@@ -29213,8 +29282,7 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
|
|
|
29213
29282
|
const wbColorName = (_Object$entries$find$ = (_Object$entries$find = Object.entries(color).find(([_, value]) => value === color$1)) == null ? void 0 : _Object$entries$find[0]) != null ? _Object$entries$find$ : "blue";
|
|
29214
29283
|
const pointClasses = `movable-point ${dragging ? "movable-point--dragging" : ""} ${focusClass(focusBehavior)}`;
|
|
29215
29284
|
const [[x, y]] = useTransformVectorsToPixels(point);
|
|
29216
|
-
const [xMin, xMax] = range
|
|
29217
|
-
const [yMin, yMax] = range[1];
|
|
29285
|
+
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
29218
29286
|
const [[verticalStartX]] = useTransformVectorsToPixels([xMin, 0]);
|
|
29219
29287
|
const [[verticalEndX]] = useTransformVectorsToPixels([xMax, 0]);
|
|
29220
29288
|
const [[_, horizontalStartY]] = useTransformVectorsToPixels([0, yMin]);
|
|
@@ -29271,7 +29339,7 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
|
|
|
29271
29339
|
return /*#__PURE__*/React.createElement(React.Fragment, null, showHairlines && hairlines, showTooltips ? /*#__PURE__*/React.createElement(Tooltip$1, {
|
|
29272
29340
|
autoUpdate: true,
|
|
29273
29341
|
backgroundColor: wbColorName,
|
|
29274
|
-
content: `(${point[
|
|
29342
|
+
content: `(${point[X]}, ${point[Y]})`,
|
|
29275
29343
|
contentStyle: {
|
|
29276
29344
|
color: "white"
|
|
29277
29345
|
}
|
|
@@ -29456,16 +29524,7 @@ function trimRange(range, graphDimensionsInPixels) {
|
|
|
29456
29524
|
const graphUnitsPerPixelY = size(yRange) / pixelsTall;
|
|
29457
29525
|
const graphUnitsToTrimX = pixelsToTrim * graphUnitsPerPixelX;
|
|
29458
29526
|
const graphUnitsToTrimY = pixelsToTrim * graphUnitsPerPixelY;
|
|
29459
|
-
return [
|
|
29460
|
-
}
|
|
29461
|
-
function trim(interval, amount) {
|
|
29462
|
-
if (size(interval) < amount * 2) {
|
|
29463
|
-
return [0, 0];
|
|
29464
|
-
}
|
|
29465
|
-
return [interval[0] + amount, interval[1] - amount];
|
|
29466
|
-
}
|
|
29467
|
-
function size(interval) {
|
|
29468
|
-
return interval[1] - interval[0];
|
|
29527
|
+
return inset([graphUnitsToTrimX, graphUnitsToTrimY], range);
|
|
29469
29528
|
}
|
|
29470
29529
|
|
|
29471
29530
|
const SegmentGraph = props => {
|
|
@@ -29598,7 +29657,7 @@ const SvgDefs = () => /*#__PURE__*/React__default.createElement("defs", null, /*
|
|
|
29598
29657
|
in: "SourceGraphic"
|
|
29599
29658
|
})));
|
|
29600
29659
|
|
|
29601
|
-
const
|
|
29660
|
+
const PolygonAngle = ({
|
|
29602
29661
|
centerPoint,
|
|
29603
29662
|
endPoints,
|
|
29604
29663
|
range,
|
|
@@ -29625,7 +29684,7 @@ const Angle = ({
|
|
|
29625
29684
|
// Used to calculate the inside of the angle and to create square
|
|
29626
29685
|
const [x3, y3] = vec.add(centerPoint, vec.add(vec.sub([x1, y1], centerPoint), vec.sub([x2, y2], centerPoint)));
|
|
29627
29686
|
if (!showAngles) {
|
|
29628
|
-
return
|
|
29687
|
+
return isRightPolygonAngle(angle) ? /*#__PURE__*/React.createElement(RightAngleSquare, {
|
|
29629
29688
|
start: [x1, y1],
|
|
29630
29689
|
vertex: [x2, y2],
|
|
29631
29690
|
end: [x3, y3]
|
|
@@ -29662,17 +29721,101 @@ const Angle = ({
|
|
|
29662
29721
|
start: [x1, y1],
|
|
29663
29722
|
vertex: [x2, y2],
|
|
29664
29723
|
end: [x3, y3]
|
|
29665
|
-
}) : /*#__PURE__*/React.createElement(
|
|
29666
|
-
|
|
29667
|
-
|
|
29668
|
-
fill: "none"
|
|
29669
|
-
})), /*#__PURE__*/React.createElement(TextLabel, {
|
|
29724
|
+
}) : /*#__PURE__*/React.createElement(Arc, {
|
|
29725
|
+
arc: arc
|
|
29726
|
+
}), /*#__PURE__*/React.createElement(TextLabel, {
|
|
29670
29727
|
x: x3,
|
|
29671
29728
|
y: y3,
|
|
29672
29729
|
attach: y3 - centerY > 0 ? "s" : "n",
|
|
29673
29730
|
attachDistance: Math.abs(y3 - centerY) < 0.2 || vec.dist([x3, y3], centerPoint) < 0.3 ? 20 : 10
|
|
29674
29731
|
}, angleLabel, "\xB0"));
|
|
29675
29732
|
};
|
|
29733
|
+
const Angle = ({
|
|
29734
|
+
vertex,
|
|
29735
|
+
coords,
|
|
29736
|
+
showAngles,
|
|
29737
|
+
allowReflexAngles,
|
|
29738
|
+
range
|
|
29739
|
+
}) => {
|
|
29740
|
+
// Check if the points are clockwise or not, depending on whether we allow reflex angles
|
|
29741
|
+
const areClockwise = clockwise$1([...coords, vertex]);
|
|
29742
|
+
const shouldReverseCoords = areClockwise && !allowReflexAngles;
|
|
29743
|
+
|
|
29744
|
+
// Reverse the coordinates accordingly to ensure the angle is calculated correctly
|
|
29745
|
+
const clockwiseCoords = shouldReverseCoords ? coords : coords.reverse();
|
|
29746
|
+
|
|
29747
|
+
// Calculate the angles between the two points
|
|
29748
|
+
const startAngle = findAngle(clockwiseCoords[0], vertex);
|
|
29749
|
+
const endAngle = findAngle(clockwiseCoords[1], vertex);
|
|
29750
|
+
const angle = (startAngle + 360 - endAngle) % 360;
|
|
29751
|
+
|
|
29752
|
+
// Check if the angle is reflexive
|
|
29753
|
+
const isReflexive = angle > 180;
|
|
29754
|
+
|
|
29755
|
+
// Break out the necessary points for the arc calculation
|
|
29756
|
+
const [centerX, centerY] = vertex;
|
|
29757
|
+
const [point1, point2] = clockwiseCoords;
|
|
29758
|
+
const [startX, startY] = point1;
|
|
29759
|
+
const [endX, endY] = point2;
|
|
29760
|
+
const a = vec.dist(vertex, point1);
|
|
29761
|
+
const b = vec.dist(vertex, point2);
|
|
29762
|
+
|
|
29763
|
+
// Set the radius of the arc
|
|
29764
|
+
const radius = 2;
|
|
29765
|
+
|
|
29766
|
+
// Calculate the end points of the arc
|
|
29767
|
+
const y1 = centerY + (startY - centerY) / a * radius;
|
|
29768
|
+
const x2 = centerX + (endX - centerX) / b * radius;
|
|
29769
|
+
const x1 = centerX + (startX - centerX) / a * radius;
|
|
29770
|
+
const y2 = centerY + (endY - centerY) / b * radius;
|
|
29771
|
+
|
|
29772
|
+
// Used to calculate the inside of the angle and to create square
|
|
29773
|
+
const [x3, y3] = vec.add(vertex, vec.add(vec.sub([x1, y1], vertex), vec.sub([x2, y2], vertex)));
|
|
29774
|
+
|
|
29775
|
+
// Determine whether the arc should be drawn outside the angle
|
|
29776
|
+
const isOutside = shouldDrawArcOutside([x3, y3], vertex, range, [[vertex, point1], [vertex, point2]]);
|
|
29777
|
+
|
|
29778
|
+
// Determine the flags for the arc
|
|
29779
|
+
// If the angle is outside OR reflexive, we want to draw a large arc
|
|
29780
|
+
// If the angle is outside AND reflexive, we want to sweep the arc
|
|
29781
|
+
const largeArcFlag = isOutside || isReflexive ? 1 : 0;
|
|
29782
|
+
const sweepFlag = isOutside && isReflexive ? 1 : 0;
|
|
29783
|
+
|
|
29784
|
+
// Create the SVG path for the arc
|
|
29785
|
+
const arc = `M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${x2} ${y2}`;
|
|
29786
|
+
|
|
29787
|
+
// We only ever want to show whole angles
|
|
29788
|
+
const angleLabel = parseFloat(angle.toFixed(0)); // Only want to show whole angles
|
|
29789
|
+
|
|
29790
|
+
// Calculate the text position based on the angle and whether we allow reflex angles
|
|
29791
|
+
// Let's try the angle bisector method to find the midpoint of the arc
|
|
29792
|
+
const [textX, textY] = calculateBisectorPoint(point1, point2, vertex, isReflexive, allowReflexAngles, radius);
|
|
29793
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("filter", {
|
|
29794
|
+
id: "background",
|
|
29795
|
+
x: "-5%",
|
|
29796
|
+
width: "110%",
|
|
29797
|
+
y: "0%",
|
|
29798
|
+
height: "100%"
|
|
29799
|
+
}, /*#__PURE__*/React.createElement("feFlood", {
|
|
29800
|
+
floodColor: "#FFF",
|
|
29801
|
+
floodOpacity: "0.5"
|
|
29802
|
+
}), /*#__PURE__*/React.createElement("feComposite", {
|
|
29803
|
+
operator: "over",
|
|
29804
|
+
in: "SourceGraphic"
|
|
29805
|
+
}))), isRightAngle(angle) ? /*#__PURE__*/React.createElement(RightAngleSquare, {
|
|
29806
|
+
start: [x1, y1],
|
|
29807
|
+
vertex: [x2, y2],
|
|
29808
|
+
end: [x3, y3],
|
|
29809
|
+
className: "arc-right-angle"
|
|
29810
|
+
}) : /*#__PURE__*/React.createElement(Arc, {
|
|
29811
|
+
arc: arc,
|
|
29812
|
+
className: "angle-arc"
|
|
29813
|
+
}), showAngles && /*#__PURE__*/React.createElement(TextLabel, {
|
|
29814
|
+
x: textX,
|
|
29815
|
+
y: textY,
|
|
29816
|
+
color: color.blue
|
|
29817
|
+
}, angleLabel, "\xB0"));
|
|
29818
|
+
};
|
|
29676
29819
|
|
|
29677
29820
|
/**
|
|
29678
29821
|
* This is broken out into its own component so that it can be used for an early return
|
|
@@ -29681,13 +29824,30 @@ const Angle = ({
|
|
|
29681
29824
|
const RightAngleSquare = ({
|
|
29682
29825
|
start: [x1, y1],
|
|
29683
29826
|
vertex: [x2, y2],
|
|
29684
|
-
end: [x3, y3]
|
|
29827
|
+
end: [x3, y3],
|
|
29828
|
+
className
|
|
29685
29829
|
}) => /*#__PURE__*/React.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React.createElement("path", {
|
|
29686
29830
|
d: `M ${x1} ${y1} L ${x3} ${y3} M ${x3} ${y3} L ${x2} ${y2}`,
|
|
29687
29831
|
strokeWidth: 0.02,
|
|
29688
|
-
fill: "none"
|
|
29832
|
+
fill: "none",
|
|
29833
|
+
className: className
|
|
29689
29834
|
}));
|
|
29690
|
-
|
|
29835
|
+
|
|
29836
|
+
// We're conditionally adding the class name here so that we can style the arc differently
|
|
29837
|
+
// based on whether it's an angle or a polygon angle
|
|
29838
|
+
const Arc = ({
|
|
29839
|
+
arc,
|
|
29840
|
+
className
|
|
29841
|
+
}) => {
|
|
29842
|
+
return /*#__PURE__*/React.createElement(MafsCssTransformWrapper, null, /*#__PURE__*/React.createElement("path", {
|
|
29843
|
+
d: arc,
|
|
29844
|
+
strokeWidth: 0.02,
|
|
29845
|
+
fill: "none",
|
|
29846
|
+
className: className
|
|
29847
|
+
}));
|
|
29848
|
+
};
|
|
29849
|
+
const isRightPolygonAngle = angle => Math.abs(angle - Math.PI / 2) < 0.01;
|
|
29850
|
+
const isRightAngle = angle => Math.round(angle) === 90;
|
|
29691
29851
|
|
|
29692
29852
|
/**
|
|
29693
29853
|
* Determines if an angle is an inside (false) or outside (true) angle.
|
|
@@ -29701,25 +29861,81 @@ const shouldDrawArcOutside = (midpoint, vertex, range, polygonLines) => {
|
|
|
29701
29861
|
// Create a ray from the midpoint (inside angle) to the edge of the range
|
|
29702
29862
|
const rangeIntersectionPoint = getIntersectionOfRayWithBox(midpoint, vertex, range);
|
|
29703
29863
|
let lineIntersectionCount = 0;
|
|
29704
|
-
polygonLines.forEach(line =>
|
|
29864
|
+
polygonLines.forEach(line => segmentsIntersect([vertex, rangeIntersectionPoint], line) && lineIntersectionCount++);
|
|
29705
29865
|
|
|
29706
29866
|
// If the number of intersections is even, the angle is inside the polygon
|
|
29707
29867
|
return !isEven(lineIntersectionCount);
|
|
29708
29868
|
};
|
|
29869
|
+
const isEven = n => n % 2 === 0;
|
|
29709
29870
|
|
|
29710
|
-
//
|
|
29711
|
-
//
|
|
29712
|
-
|
|
29713
|
-
|
|
29714
|
-
|
|
29715
|
-
|
|
29871
|
+
// This function calculates the bisector point of an angle formed by two points
|
|
29872
|
+
// and a vertex. It is used to position the angle label so that it is always
|
|
29873
|
+
// outside the arc at the exact midpoint of the angle.
|
|
29874
|
+
// Reflex angles make use of polar coordinates to determine the correct direction
|
|
29875
|
+
function calculateBisectorPoint(point1, point2, vertex, isReflex, allowReflex, arcRadius) {
|
|
29876
|
+
const [originX, originY] = vertex;
|
|
29877
|
+
const [x1, y1] = point1;
|
|
29878
|
+
const [x2, y2] = point2;
|
|
29879
|
+
|
|
29880
|
+
// Convert the Cartesian Coordinates to vectors
|
|
29881
|
+
const vectorA = [x1 - originX, y1 - originY];
|
|
29882
|
+
const vectorB = [x2 - originX, y2 - originY];
|
|
29883
|
+
|
|
29884
|
+
// Calculate the average angle of the vectors
|
|
29885
|
+
// so that we can point the bisector in the correct direction using polar coordinates
|
|
29886
|
+
const angleA = Math.atan2(vectorA[1], vectorA[0]);
|
|
29887
|
+
const angleB = Math.atan2(vectorB[1], vectorB[0]);
|
|
29888
|
+
|
|
29889
|
+
// Calculate the average of these two angles
|
|
29890
|
+
let averageAngle = (angleA + angleB) / 2;
|
|
29891
|
+
const angleRadians = Math.abs(angleA - angleB);
|
|
29892
|
+
|
|
29893
|
+
// Adjust the average angle to point in the correct direction
|
|
29894
|
+
// and to account for reflex angles and a bug with 180 degrees
|
|
29895
|
+
// that occurs due to our angleOffsetDegrees option in the graph
|
|
29896
|
+
if (allowReflex) {
|
|
29897
|
+
// If the resulting angles are less than 180 degrees,
|
|
29898
|
+
// or if the user has flipped the points on the interactive element
|
|
29899
|
+
// ensure that we have the correct direction for the reflex angle
|
|
29900
|
+
if (angleRadians <= Math.PI && isReflex || angleB > angleA) {
|
|
29901
|
+
averageAngle += Math.PI; // Add 180 degrees
|
|
29902
|
+
}
|
|
29716
29903
|
} else {
|
|
29717
|
-
|
|
29718
|
-
|
|
29719
|
-
|
|
29904
|
+
// If the resulting angles are greater than 180 degrees, adjust
|
|
29905
|
+
// to get the correct angle, as we already know that the angle is not reflexive
|
|
29906
|
+
// This fixes a bug that occurs at 180 degrees due to our angleOffsetDegrees option in the graph
|
|
29907
|
+
if (angleRadians > Math.PI) {
|
|
29908
|
+
averageAngle -= Math.PI; // Subract 180 degrees
|
|
29909
|
+
}
|
|
29720
29910
|
}
|
|
29721
|
-
|
|
29722
|
-
|
|
29911
|
+
|
|
29912
|
+
// Convert the average angle back to Cartesian coordinates
|
|
29913
|
+
const sum = [Math.cos(averageAngle), Math.sin(averageAngle)];
|
|
29914
|
+
|
|
29915
|
+
// Calculate the magnitude of the sum to normalize it
|
|
29916
|
+
const sumMagnitude = Math.sqrt(sum[0] ** 2 + sum[1] ** 2);
|
|
29917
|
+
const bisectorDirection = [sum[0] / sumMagnitude, sum[1] / sumMagnitude];
|
|
29918
|
+
|
|
29919
|
+
// Calculate the initial distance of the bisector direction from the origin
|
|
29920
|
+
const initialDistance = Math.sqrt(bisectorDirection[0] ** 2 + bisectorDirection[1] ** 2);
|
|
29921
|
+
|
|
29922
|
+
// Determine the minimum radius to ensure that the text is always outside the arc
|
|
29923
|
+
const requiredDistance = arcRadius * 1.75;
|
|
29924
|
+
let radius = requiredDistance / initialDistance;
|
|
29925
|
+
|
|
29926
|
+
// Apply radius only if the initial distance is less than required
|
|
29927
|
+
if (initialDistance >= requiredDistance) {
|
|
29928
|
+
radius = 1; // Use the unit vector as is
|
|
29929
|
+
}
|
|
29930
|
+
|
|
29931
|
+
// Scale the bisector direction by the radius
|
|
29932
|
+
const bisectorPoint = [bisectorDirection[0] * radius, bisectorDirection[1] * radius];
|
|
29933
|
+
|
|
29934
|
+
// Add the vertex to the bisector point to get the final position
|
|
29935
|
+
// to ensure that the angle label moves with the interactive element
|
|
29936
|
+
const scaledBisector = vec.add(bisectorPoint, vertex);
|
|
29937
|
+
return scaledBisector;
|
|
29938
|
+
}
|
|
29723
29939
|
|
|
29724
29940
|
const StyledMovablePoint = props => {
|
|
29725
29941
|
const {
|
|
@@ -29801,7 +30017,7 @@ const PolygonGraph = props => {
|
|
|
29801
30017
|
if (!pt1 || !pt2) {
|
|
29802
30018
|
return null;
|
|
29803
30019
|
}
|
|
29804
|
-
return /*#__PURE__*/React.createElement(
|
|
30020
|
+
return /*#__PURE__*/React.createElement(PolygonAngle, {
|
|
29805
30021
|
key: "angle-" + i,
|
|
29806
30022
|
centerPoint: point,
|
|
29807
30023
|
endPoints: [pt1, pt2],
|
|
@@ -29842,6 +30058,12 @@ const PolygonGraph = props => {
|
|
|
29842
30058
|
onMove: destination => dispatch(movePoint(i, destination))
|
|
29843
30059
|
})));
|
|
29844
30060
|
};
|
|
30061
|
+
function getLines(points) {
|
|
30062
|
+
return points.map((point, i) => {
|
|
30063
|
+
const next = points[(i + 1) % points.length];
|
|
30064
|
+
return [point, next];
|
|
30065
|
+
});
|
|
30066
|
+
}
|
|
29845
30067
|
|
|
29846
30068
|
function getGradableGraph(state, initialGraph) {
|
|
29847
30069
|
if (!state.hasBeenInteractedWith) {
|
|
@@ -29893,6 +30115,12 @@ function getGradableGraph(state, initialGraph) {
|
|
|
29893
30115
|
coords: state.coords
|
|
29894
30116
|
});
|
|
29895
30117
|
}
|
|
30118
|
+
if (state.type === "angle" && initialGraph.type === "angle") {
|
|
30119
|
+
return _extends({}, initialGraph, {
|
|
30120
|
+
coords: state.coords,
|
|
30121
|
+
allowReflexAngles: state.allowReflexAngles
|
|
30122
|
+
});
|
|
30123
|
+
}
|
|
29896
30124
|
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
29897
30125
|
}
|
|
29898
30126
|
|
|
@@ -29955,16 +30183,16 @@ function MovableCircle(props) {
|
|
|
29955
30183
|
className: `movable-circle ${dragging ? "movable-circle--dragging" : ""}`
|
|
29956
30184
|
}, /*#__PURE__*/React.createElement("ellipse", {
|
|
29957
30185
|
className: "focus-ring",
|
|
29958
|
-
cx: centerPx[
|
|
29959
|
-
cy: centerPx[
|
|
29960
|
-
rx: radiiPx[
|
|
29961
|
-
ry: radiiPx[
|
|
30186
|
+
cx: centerPx[X],
|
|
30187
|
+
cy: centerPx[Y],
|
|
30188
|
+
rx: radiiPx[X] + 3,
|
|
30189
|
+
ry: radiiPx[Y] + 3
|
|
29962
30190
|
}), /*#__PURE__*/React.createElement("ellipse", {
|
|
29963
30191
|
className: "circle",
|
|
29964
|
-
cx: centerPx[
|
|
29965
|
-
cy: centerPx[
|
|
29966
|
-
rx: radiiPx[
|
|
29967
|
-
ry: radiiPx[
|
|
30192
|
+
cx: centerPx[X],
|
|
30193
|
+
cy: centerPx[Y],
|
|
30194
|
+
rx: radiiPx[X],
|
|
30195
|
+
ry: radiiPx[Y]
|
|
29968
30196
|
}), /*#__PURE__*/React.createElement(DragHandle, {
|
|
29969
30197
|
center: center
|
|
29970
30198
|
}));
|
|
@@ -29980,10 +30208,10 @@ function DragHandle(props) {
|
|
|
29980
30208
|
const topLeft = vec.sub(centerPx, vec.scale(dragHandleDimensions, 0.5));
|
|
29981
30209
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("rect", {
|
|
29982
30210
|
className: "movable-circle-handle",
|
|
29983
|
-
x: topLeft[
|
|
29984
|
-
y: topLeft[
|
|
29985
|
-
width: dragHandleDimensions[
|
|
29986
|
-
height: dragHandleDimensions[
|
|
30211
|
+
x: topLeft[X],
|
|
30212
|
+
y: topLeft[Y],
|
|
30213
|
+
width: dragHandleDimensions[X],
|
|
30214
|
+
height: dragHandleDimensions[Y],
|
|
29987
30215
|
rx: cornerRadius,
|
|
29988
30216
|
ry: cornerRadius
|
|
29989
30217
|
}), dragHandleDotPositions.map(offsetPx => {
|
|
@@ -30118,15 +30346,15 @@ const getSinusoidCoefficients = coords => {
|
|
|
30118
30346
|
const p2 = coords[1];
|
|
30119
30347
|
|
|
30120
30348
|
// If the x-coordinates are the same, we are unable to calculate the coefficients
|
|
30121
|
-
if (p2[
|
|
30349
|
+
if (p2[X] === p1[X]) {
|
|
30122
30350
|
return;
|
|
30123
30351
|
}
|
|
30124
30352
|
|
|
30125
30353
|
// Resulting coefficients are canonical for this sine curve
|
|
30126
|
-
const amplitude = p2[
|
|
30127
|
-
const angularFrequency = Math.PI / (2 * (p2[
|
|
30128
|
-
const phase = p1[
|
|
30129
|
-
const verticalOffset = p1[
|
|
30354
|
+
const amplitude = p2[Y] - p1[Y];
|
|
30355
|
+
const angularFrequency = Math.PI / (2 * (p2[X] - p1[X]));
|
|
30356
|
+
const phase = p1[X] * angularFrequency;
|
|
30357
|
+
const verticalOffset = p1[Y];
|
|
30130
30358
|
return {
|
|
30131
30359
|
amplitude,
|
|
30132
30360
|
angularFrequency,
|
|
@@ -30135,6 +30363,74 @@ const getSinusoidCoefficients = coords => {
|
|
|
30135
30363
|
};
|
|
30136
30364
|
};
|
|
30137
30365
|
|
|
30366
|
+
function AngleGraph(props) {
|
|
30367
|
+
const {
|
|
30368
|
+
dispatch,
|
|
30369
|
+
graphState
|
|
30370
|
+
} = props;
|
|
30371
|
+
const {
|
|
30372
|
+
graphDimensionsInPixels
|
|
30373
|
+
} = useGraphConfig();
|
|
30374
|
+
const {
|
|
30375
|
+
coords,
|
|
30376
|
+
showAngles,
|
|
30377
|
+
range,
|
|
30378
|
+
allowReflexAngles,
|
|
30379
|
+
angleOffsetDeg,
|
|
30380
|
+
snapDegrees
|
|
30381
|
+
} = graphState;
|
|
30382
|
+
|
|
30383
|
+
// Break the coords into the two end points and the center point
|
|
30384
|
+
const endPoints = [coords[0], coords[2]];
|
|
30385
|
+
const centerPoint = coords[1];
|
|
30386
|
+
|
|
30387
|
+
// Convert the vectors to pixels for rendering the svg lines
|
|
30388
|
+
const angleLines = [[centerPoint, endPoints[0]], [centerPoint, endPoints[1]]];
|
|
30389
|
+
const linePixelCoords = [useTransformVectorsToPixels(centerPoint, endPoints[0]), useTransformVectorsToPixels(centerPoint, endPoints[1])];
|
|
30390
|
+
|
|
30391
|
+
// Create the SVG lines for the angle
|
|
30392
|
+
const svgLines = linePixelCoords.map(([startPtPx, endPtPx], i) => {
|
|
30393
|
+
const trimmedRange = trimRange(range, graphDimensionsInPixels);
|
|
30394
|
+
const endExtend = getIntersectionOfRayWithBox(angleLines[i][0], angleLines[i][1], trimmedRange);
|
|
30395
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
30396
|
+
key: `line-${i}`
|
|
30397
|
+
}, /*#__PURE__*/React.createElement(SVGLine, {
|
|
30398
|
+
start: startPtPx,
|
|
30399
|
+
end: endPtPx,
|
|
30400
|
+
style: {
|
|
30401
|
+
stroke: "var(--movable-line-stroke-color)",
|
|
30402
|
+
strokeWidth: "var(--movable-line-stroke-weight)"
|
|
30403
|
+
}
|
|
30404
|
+
}), /*#__PURE__*/React.createElement(Vector, {
|
|
30405
|
+
tail: angleLines[i][1],
|
|
30406
|
+
tip: endExtend,
|
|
30407
|
+
color: "var(--movable-line-stroke-color)"
|
|
30408
|
+
}));
|
|
30409
|
+
});
|
|
30410
|
+
|
|
30411
|
+
// Create the angle indicator parameters
|
|
30412
|
+
const angleParams = {
|
|
30413
|
+
vertex: centerPoint,
|
|
30414
|
+
coords: endPoints,
|
|
30415
|
+
allowReflexAngles: allowReflexAngles || false,
|
|
30416
|
+
// Whether to allow reflex angles or not
|
|
30417
|
+
angleOffsetDeg: angleOffsetDeg || 0,
|
|
30418
|
+
// The angle offset from the x-axis
|
|
30419
|
+
snapDegrees: snapDegrees || 1,
|
|
30420
|
+
// The multiple of degrees to snap to
|
|
30421
|
+
range: range,
|
|
30422
|
+
showAngles: showAngles || false // Whether to show the angle or not
|
|
30423
|
+
};
|
|
30424
|
+
|
|
30425
|
+
// Render the lines, angle, and then movable points
|
|
30426
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, svgLines, /*#__PURE__*/React.createElement(Angle, angleParams), coords.map((point, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
30427
|
+
key: "point-" + i,
|
|
30428
|
+
snapTo: "angles",
|
|
30429
|
+
point: point,
|
|
30430
|
+
onMove: destination => dispatch(movePoint(i, destination))
|
|
30431
|
+
})));
|
|
30432
|
+
}
|
|
30433
|
+
|
|
30138
30434
|
// Universal padding for the axis ticks to ensure that the labels
|
|
30139
30435
|
// have enough space to render without overlapping the tick lines
|
|
30140
30436
|
const tickPadding = 5;
|
|
@@ -30144,10 +30440,8 @@ const AxisTickLabels = () => {
|
|
|
30144
30440
|
tickStep,
|
|
30145
30441
|
range
|
|
30146
30442
|
} = graphConfig;
|
|
30147
|
-
const [xMin, xMax] = range
|
|
30148
|
-
const [
|
|
30149
|
-
const yTickStep = tickStep[1];
|
|
30150
|
-
const xTickStep = tickStep[0];
|
|
30443
|
+
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
30444
|
+
const [xTickStep, yTickStep] = tickStep;
|
|
30151
30445
|
const yGridTicks = generateTickLocations$1(yTickStep, yMin, yMax);
|
|
30152
30446
|
const xGridTicks = generateTickLocations$1(xTickStep, xMin, xMax);
|
|
30153
30447
|
return /*#__PURE__*/React.createElement("div", {
|
|
@@ -30426,13 +30720,13 @@ const YGridTick = ({
|
|
|
30426
30720
|
|
|
30427
30721
|
// If the graph is zoomed in, we want to make sure the ticks are still visible
|
|
30428
30722
|
// even if they are outside the graph's range.
|
|
30429
|
-
if (graphInfo.range[
|
|
30723
|
+
if (graphInfo.range[X][MIN] > 0) {
|
|
30430
30724
|
// If the graph is on the positive side of the x-axis, lock the ticks to the left side of the graph
|
|
30431
|
-
xPointOnAxis = graphInfo.range[
|
|
30725
|
+
xPointOnAxis = graphInfo.range[X][MIN];
|
|
30432
30726
|
}
|
|
30433
|
-
if (graphInfo.range[
|
|
30727
|
+
if (graphInfo.range[X][MAX] < 0) {
|
|
30434
30728
|
// If the graph is on the negative side of the x-axis, lock the ticks to the right side of the graph
|
|
30435
|
-
xPointOnAxis = graphInfo.range[
|
|
30729
|
+
xPointOnAxis = graphInfo.range[X][MAX];
|
|
30436
30730
|
}
|
|
30437
30731
|
const pointOnAxis = [xPointOnAxis, y];
|
|
30438
30732
|
const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
|
|
@@ -30466,13 +30760,13 @@ const XGridTick = ({
|
|
|
30466
30760
|
let yPointOnAxis = 0;
|
|
30467
30761
|
// If the graph is zoomed in, we want to make sure the ticks are still visible
|
|
30468
30762
|
// even if they are outside the graph's range.
|
|
30469
|
-
if (graphInfo.range[
|
|
30763
|
+
if (graphInfo.range[Y][MIN] > 0) {
|
|
30470
30764
|
// If the graph is on the positive side of the y-axis, lock the ticks to the top of the graph
|
|
30471
|
-
yPointOnAxis = graphInfo.range[
|
|
30765
|
+
yPointOnAxis = graphInfo.range[Y][MIN];
|
|
30472
30766
|
}
|
|
30473
|
-
if (graphInfo.range[
|
|
30767
|
+
if (graphInfo.range[Y][MAX] < 0) {
|
|
30474
30768
|
// If the graph is on the negative side of the x-axis, lock the ticks to the bottom of the graph
|
|
30475
|
-
yPointOnAxis = graphInfo.range[
|
|
30769
|
+
yPointOnAxis = graphInfo.range[Y][MAX];
|
|
30476
30770
|
}
|
|
30477
30771
|
const pointOnAxis = [x, yPointOnAxis];
|
|
30478
30772
|
const [[xPosition, yPosition]] = useTransformVectorsToPixels(pointOnAxis);
|
|
@@ -30527,10 +30821,8 @@ const AxisTicks = () => {
|
|
|
30527
30821
|
width,
|
|
30528
30822
|
height
|
|
30529
30823
|
};
|
|
30530
|
-
const [xMin, xMax] = range
|
|
30531
|
-
const [
|
|
30532
|
-
const yTickStep = tickStep[1];
|
|
30533
|
-
const xTickStep = tickStep[0];
|
|
30824
|
+
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
30825
|
+
const [xTickStep, yTickStep] = tickStep;
|
|
30534
30826
|
const yGridTicks = generateTickLocations(yTickStep, yMin, yMax);
|
|
30535
30827
|
const xGridTicks = generateTickLocations(xTickStep, xMin, xMax);
|
|
30536
30828
|
return /*#__PURE__*/React.createElement("g", {
|
|
@@ -30569,8 +30861,8 @@ const axisOptions = (props, axisIndex) => {
|
|
|
30569
30861
|
};
|
|
30570
30862
|
const Grid = props => {
|
|
30571
30863
|
return props.markings === "none" ? null : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Coordinates.Cartesian, {
|
|
30572
|
-
xAxis: axisOptions(props,
|
|
30573
|
-
yAxis: axisOptions(props,
|
|
30864
|
+
xAxis: axisOptions(props, X),
|
|
30865
|
+
yAxis: axisOptions(props, Y)
|
|
30574
30866
|
}),
|
|
30575
30867
|
// Only render the axis ticks and arrows if the markings are set to a full "graph"
|
|
30576
30868
|
props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)));
|
|
@@ -31179,7 +31471,7 @@ const LegacyGrid = ({
|
|
|
31179
31471
|
height
|
|
31180
31472
|
} = backgroundImage != null ? backgroundImage : {};
|
|
31181
31473
|
if (url && typeof url === "string") {
|
|
31182
|
-
const scale = box[
|
|
31474
|
+
const scale = box[X] / interactiveSizes$1.defaultBoxSize;
|
|
31183
31475
|
return /*#__PURE__*/React.createElement(View, {
|
|
31184
31476
|
style: {
|
|
31185
31477
|
position: "absolute",
|
|
@@ -31244,9 +31536,9 @@ function Protractor() {
|
|
|
31244
31536
|
const angle = calculateAngleInDegrees(rotationHandleOffset) - calculateAngleInDegrees(centerToRotationHandle);
|
|
31245
31537
|
return /*#__PURE__*/React.createElement("g", {
|
|
31246
31538
|
ref: draggableRef,
|
|
31247
|
-
transform: `translate(${topLeftPx[
|
|
31539
|
+
transform: `translate(${topLeftPx[X]}, ${topLeftPx[Y]}), rotate(${angle})`,
|
|
31248
31540
|
style: {
|
|
31249
|
-
transformOrigin: `${-centerToTopLeft[
|
|
31541
|
+
transformOrigin: `${-centerToTopLeft[X]}px ${-centerToTopLeft[Y]}px`
|
|
31250
31542
|
}
|
|
31251
31543
|
}, /*#__PURE__*/React.createElement("image", {
|
|
31252
31544
|
href: protractorImage
|
|
@@ -31258,7 +31550,7 @@ function Protractor() {
|
|
|
31258
31550
|
function RotationArrow() {
|
|
31259
31551
|
const radius = 175;
|
|
31260
31552
|
const angleDeg = 10;
|
|
31261
|
-
const angleRad =
|
|
31553
|
+
const angleRad = convertDegreesToRadians(angleDeg);
|
|
31262
31554
|
const endX = radius * (1 - Math.cos(angleRad));
|
|
31263
31555
|
const endY = radius * -Math.sin(angleRad);
|
|
31264
31556
|
const rotationArrow = pathBuilder().move(0, 0).circularArc(radius, endX, endY, {
|
|
@@ -31337,17 +31629,135 @@ function useDraggablePx(args) {
|
|
|
31337
31629
|
});
|
|
31338
31630
|
}
|
|
31339
31631
|
|
|
31340
|
-
|
|
31341
|
-
|
|
31342
|
-
|
|
31343
|
-
|
|
31344
|
-
|
|
31345
|
-
|
|
31346
|
-
|
|
31347
|
-
|
|
31348
|
-
|
|
31349
|
-
|
|
31350
|
-
|
|
31632
|
+
const MafsGraph = props => {
|
|
31633
|
+
const {
|
|
31634
|
+
state,
|
|
31635
|
+
dispatch,
|
|
31636
|
+
labels
|
|
31637
|
+
} = props;
|
|
31638
|
+
const [width, height] = props.box;
|
|
31639
|
+
const tickStep = props.step;
|
|
31640
|
+
return /*#__PURE__*/React.createElement(GraphConfigContext.Provider, {
|
|
31641
|
+
value: {
|
|
31642
|
+
range: state.range,
|
|
31643
|
+
snapStep: state.snapStep,
|
|
31644
|
+
markings: props.markings,
|
|
31645
|
+
tickStep: tickStep,
|
|
31646
|
+
gridStep: props.gridStep,
|
|
31647
|
+
showTooltips: !!props.showTooltips,
|
|
31648
|
+
graphDimensionsInPixels: props.box,
|
|
31649
|
+
width,
|
|
31650
|
+
height,
|
|
31651
|
+
labels
|
|
31652
|
+
}
|
|
31653
|
+
}, /*#__PURE__*/React.createElement(View, {
|
|
31654
|
+
className: "mafs-graph",
|
|
31655
|
+
style: {
|
|
31656
|
+
width,
|
|
31657
|
+
height,
|
|
31658
|
+
position: "relative",
|
|
31659
|
+
padding: "25px 25px 0 0",
|
|
31660
|
+
boxSizing: "content-box",
|
|
31661
|
+
marginLeft: "20px",
|
|
31662
|
+
marginBottom: "20px"
|
|
31663
|
+
}
|
|
31664
|
+
}, /*#__PURE__*/React.createElement(LegacyGrid, {
|
|
31665
|
+
box: props.box,
|
|
31666
|
+
backgroundImage: props.backgroundImage
|
|
31667
|
+
}), /*#__PURE__*/React.createElement(View, {
|
|
31668
|
+
style: {
|
|
31669
|
+
position: "absolute",
|
|
31670
|
+
bottom: 0,
|
|
31671
|
+
left: 0
|
|
31672
|
+
}
|
|
31673
|
+
}, props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisLabels, null), /*#__PURE__*/React.createElement(AxisTickLabels, null)), /*#__PURE__*/React.createElement(Mafs, {
|
|
31674
|
+
preserveAspectRatio: false,
|
|
31675
|
+
viewBox: {
|
|
31676
|
+
x: state.range[X],
|
|
31677
|
+
y: state.range[Y],
|
|
31678
|
+
padding: 0
|
|
31679
|
+
},
|
|
31680
|
+
pan: false,
|
|
31681
|
+
zoom: false,
|
|
31682
|
+
width: width,
|
|
31683
|
+
height: height
|
|
31684
|
+
}, /*#__PURE__*/React.createElement(SvgDefs, null), /*#__PURE__*/React.createElement(Grid, {
|
|
31685
|
+
tickStep: props.step,
|
|
31686
|
+
gridStep: props.gridStep,
|
|
31687
|
+
range: state.range,
|
|
31688
|
+
containerSizeClass: props.containerSizeClass,
|
|
31689
|
+
markings: props.markings
|
|
31690
|
+
}), props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
|
|
31691
|
+
lockedFigures: props.lockedFigures,
|
|
31692
|
+
range: state.range
|
|
31693
|
+
}), "/* Protractor */", props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
|
|
31694
|
+
state,
|
|
31695
|
+
dispatch
|
|
31696
|
+
})))));
|
|
31697
|
+
};
|
|
31698
|
+
const renderGraph = props => {
|
|
31699
|
+
const {
|
|
31700
|
+
state,
|
|
31701
|
+
dispatch
|
|
31702
|
+
} = props;
|
|
31703
|
+
const {
|
|
31704
|
+
type
|
|
31705
|
+
} = state;
|
|
31706
|
+
switch (type) {
|
|
31707
|
+
case "angle":
|
|
31708
|
+
return /*#__PURE__*/React.createElement(AngleGraph, {
|
|
31709
|
+
graphState: state,
|
|
31710
|
+
dispatch: dispatch
|
|
31711
|
+
});
|
|
31712
|
+
case "segment":
|
|
31713
|
+
return /*#__PURE__*/React.createElement(SegmentGraph, {
|
|
31714
|
+
graphState: state,
|
|
31715
|
+
dispatch: dispatch
|
|
31716
|
+
});
|
|
31717
|
+
case "linear-system":
|
|
31718
|
+
return /*#__PURE__*/React.createElement(LinearSystemGraph, {
|
|
31719
|
+
graphState: state,
|
|
31720
|
+
dispatch: dispatch
|
|
31721
|
+
});
|
|
31722
|
+
case "linear":
|
|
31723
|
+
return /*#__PURE__*/React.createElement(LinearGraph, {
|
|
31724
|
+
graphState: state,
|
|
31725
|
+
dispatch: dispatch
|
|
31726
|
+
});
|
|
31727
|
+
case "ray":
|
|
31728
|
+
return /*#__PURE__*/React.createElement(RayGraph, {
|
|
31729
|
+
graphState: state,
|
|
31730
|
+
dispatch: dispatch
|
|
31731
|
+
});
|
|
31732
|
+
case "polygon":
|
|
31733
|
+
return /*#__PURE__*/React.createElement(PolygonGraph, {
|
|
31734
|
+
graphState: state,
|
|
31735
|
+
dispatch: dispatch
|
|
31736
|
+
});
|
|
31737
|
+
case "point":
|
|
31738
|
+
return /*#__PURE__*/React.createElement(PointGraph, {
|
|
31739
|
+
graphState: state,
|
|
31740
|
+
dispatch: dispatch
|
|
31741
|
+
});
|
|
31742
|
+
case "circle":
|
|
31743
|
+
return /*#__PURE__*/React.createElement(CircleGraph, {
|
|
31744
|
+
graphState: state,
|
|
31745
|
+
dispatch: dispatch
|
|
31746
|
+
});
|
|
31747
|
+
case "quadratic":
|
|
31748
|
+
return /*#__PURE__*/React.createElement(QuadraticGraph, {
|
|
31749
|
+
graphState: state,
|
|
31750
|
+
dispatch: dispatch
|
|
31751
|
+
});
|
|
31752
|
+
case "sinusoid":
|
|
31753
|
+
return /*#__PURE__*/React.createElement(SinusoidGraph, {
|
|
31754
|
+
graphState: state,
|
|
31755
|
+
dispatch: dispatch
|
|
31756
|
+
});
|
|
31757
|
+
default:
|
|
31758
|
+
return new UnreachableCaseError(type);
|
|
31759
|
+
}
|
|
31760
|
+
};
|
|
31351
31761
|
|
|
31352
31762
|
function initializeGraphState(params) {
|
|
31353
31763
|
var _graph$snapTo;
|
|
@@ -31411,7 +31821,18 @@ function initializeGraphState(params) {
|
|
|
31411
31821
|
coords: getSinusoidCoords(graph, range, step)
|
|
31412
31822
|
});
|
|
31413
31823
|
case "angle":
|
|
31414
|
-
|
|
31824
|
+
return _extends({}, shared, {
|
|
31825
|
+
type: graph.type,
|
|
31826
|
+
showAngles: Boolean(graph.showAngles),
|
|
31827
|
+
coords: getAngleCoords({
|
|
31828
|
+
graph,
|
|
31829
|
+
range,
|
|
31830
|
+
step
|
|
31831
|
+
}),
|
|
31832
|
+
angleOffsetDeg: Number(graph.angleOffsetDeg),
|
|
31833
|
+
allowReflexAngles: Boolean(graph.allowReflexAngles),
|
|
31834
|
+
snapDegrees: Number(graph.snapDegrees)
|
|
31835
|
+
});
|
|
31415
31836
|
default:
|
|
31416
31837
|
throw new UnreachableCaseError(graph);
|
|
31417
31838
|
}
|
|
@@ -31545,6 +31966,43 @@ function getCircleCoords(graph) {
|
|
|
31545
31966
|
radiusPoint: [2, 0]
|
|
31546
31967
|
};
|
|
31547
31968
|
}
|
|
31969
|
+
const getAngleCoords = params => {
|
|
31970
|
+
const {
|
|
31971
|
+
graph,
|
|
31972
|
+
range,
|
|
31973
|
+
step
|
|
31974
|
+
} = params;
|
|
31975
|
+
if (graph.coords) {
|
|
31976
|
+
return graph.coords;
|
|
31977
|
+
}
|
|
31978
|
+
const {
|
|
31979
|
+
snapDegrees,
|
|
31980
|
+
angleOffsetDeg
|
|
31981
|
+
} = graph;
|
|
31982
|
+
const snap = snapDegrees || 1;
|
|
31983
|
+
let angle = snap;
|
|
31984
|
+
while (angle < 20) {
|
|
31985
|
+
angle += snap;
|
|
31986
|
+
}
|
|
31987
|
+
angle = angle * Math.PI / 180;
|
|
31988
|
+
const offset = (angleOffsetDeg || 0) * Math.PI / 180;
|
|
31989
|
+
let defaultCoords = [[0.85, 0.5], [0.5, 0.5]];
|
|
31990
|
+
defaultCoords = normalizePoints(range, step, defaultCoords, true);
|
|
31991
|
+
|
|
31992
|
+
// @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.
|
|
31993
|
+
const radius = magnitude(vector(...defaultCoords));
|
|
31994
|
+
|
|
31995
|
+
// We're adding a placeholder for the third point to appease ts and so that we
|
|
31996
|
+
// can calculate it after we've adjusted the first point by the angleOffsetDeg
|
|
31997
|
+
const coords = [...defaultCoords, [0, 0]];
|
|
31998
|
+
|
|
31999
|
+
// Adjust the lower point by angleOffsetDeg degrees
|
|
32000
|
+
coords[0] = [coords[1][0] + radius * Math.cos(offset), coords[1][1] + radius * Math.sin(offset)];
|
|
32001
|
+
// Position the upper point angle radians from the
|
|
32002
|
+
// lower point
|
|
32003
|
+
coords[2] = [coords[1][0] + radius * Math.cos(angle + offset), coords[1][1] + radius * Math.sin(angle + offset)];
|
|
32004
|
+
return coords;
|
|
32005
|
+
};
|
|
31548
32006
|
|
|
31549
32007
|
function interactiveGraphReducer(state, action) {
|
|
31550
32008
|
switch (action.type) {
|
|
@@ -31606,6 +32064,7 @@ function doMoveControlPoint(state, action) {
|
|
|
31606
32064
|
coords: newCoords
|
|
31607
32065
|
});
|
|
31608
32066
|
}
|
|
32067
|
+
case "angle":
|
|
31609
32068
|
case "circle":
|
|
31610
32069
|
throw new Error("FIXME implement circle reducer");
|
|
31611
32070
|
case "point":
|
|
@@ -31707,12 +32166,30 @@ function doMoveAll(state, action) {
|
|
|
31707
32166
|
}
|
|
31708
32167
|
function doMovePoint(state, action) {
|
|
31709
32168
|
switch (state.type) {
|
|
32169
|
+
case "angle":
|
|
32170
|
+
// If the index is 1, we are moving the vertex of the angle,
|
|
32171
|
+
// which will move the other two points as well
|
|
32172
|
+
if (action.index === 1) {
|
|
32173
|
+
const updatedCoords = boundAndSnapAngleVertex(state, action);
|
|
32174
|
+
return _extends({}, state, {
|
|
32175
|
+
hasBeenInteractedWith: true,
|
|
32176
|
+
coords: updatedCoords
|
|
32177
|
+
});
|
|
32178
|
+
}
|
|
32179
|
+
return _extends({}, state, {
|
|
32180
|
+
hasBeenInteractedWith: true,
|
|
32181
|
+
coords: setAtIndex({
|
|
32182
|
+
array: state.coords,
|
|
32183
|
+
index: action.index,
|
|
32184
|
+
newValue: boundAndSnapAngleEndPoints(action.destination, state, action.index)
|
|
32185
|
+
})
|
|
32186
|
+
});
|
|
31710
32187
|
case "polygon":
|
|
31711
32188
|
let newValue;
|
|
31712
32189
|
if (state.snapTo === "sides") {
|
|
31713
32190
|
newValue = boundAndSnapToSides(action.destination, state, action.index);
|
|
31714
32191
|
} else if (state.snapTo === "angles") {
|
|
31715
|
-
newValue =
|
|
32192
|
+
newValue = boundAndSnapToPolygonAngle(action.destination, state, action.index);
|
|
31716
32193
|
} else {
|
|
31717
32194
|
newValue = boundAndSnapToGrid(action.destination, state);
|
|
31718
32195
|
}
|
|
@@ -31751,7 +32228,7 @@ function doMovePoint(state, action) {
|
|
|
31751
32228
|
// vertical line. If they are, then we don't want to move the point
|
|
31752
32229
|
const _newCoords = [...state.coords];
|
|
31753
32230
|
_newCoords[action.index] = boundDestination;
|
|
31754
|
-
if (_newCoords[0][
|
|
32231
|
+
if (_newCoords[0][X] === _newCoords[1][X]) {
|
|
31755
32232
|
return state;
|
|
31756
32233
|
}
|
|
31757
32234
|
return _extends({}, state, {
|
|
@@ -31809,13 +32286,13 @@ function doMoveCenter(state, action) {
|
|
|
31809
32286
|
// if it otherwise would be off the chart
|
|
31810
32287
|
// ex: if the handle is on the right and we move the center
|
|
31811
32288
|
// to the rightmost position, move the handle to the left
|
|
31812
|
-
const [xMin, xMax] = state.range[
|
|
32289
|
+
const [xMin, xMax] = state.range[X];
|
|
31813
32290
|
const [radX] = newRadiusPoint;
|
|
31814
32291
|
if (radX < xMin || radX > xMax) {
|
|
31815
|
-
const xJumpDist = (radX - constrainedCenter[
|
|
32292
|
+
const xJumpDist = (radX - constrainedCenter[X]) * 2;
|
|
31816
32293
|
const possibleNewX = radX - xJumpDist;
|
|
31817
32294
|
if (possibleNewX >= xMin && possibleNewX <= xMax) {
|
|
31818
|
-
newRadiusPoint[
|
|
32295
|
+
newRadiusPoint[X] = possibleNewX;
|
|
31819
32296
|
}
|
|
31820
32297
|
}
|
|
31821
32298
|
return _extends({}, state, {
|
|
@@ -31832,11 +32309,11 @@ function doMoveRadiusPoint(state, action) {
|
|
|
31832
32309
|
switch (state.type) {
|
|
31833
32310
|
case "circle":
|
|
31834
32311
|
{
|
|
31835
|
-
const [xMin, xMax] = state.range[
|
|
32312
|
+
const [xMin, xMax] = state.range[X];
|
|
31836
32313
|
const nextRadiusPoint = [
|
|
31837
32314
|
// Constrain to graph range
|
|
31838
32315
|
// The +0 is to convert -0 to +0
|
|
31839
|
-
|
|
32316
|
+
clamp(action.destination[X] + 0, xMin, xMax), state.center[1]];
|
|
31840
32317
|
if (_.isEqual(nextRadiusPoint, state.center)) {
|
|
31841
32318
|
return state;
|
|
31842
32319
|
}
|
|
@@ -31871,10 +32348,10 @@ function doChangeRange(state, action) {
|
|
|
31871
32348
|
}
|
|
31872
32349
|
const getDeltaVertex = (maxMoves, minMoves, delta) => {
|
|
31873
32350
|
const [deltaX, deltaY] = delta;
|
|
31874
|
-
const maxXMove = Math.min(...maxMoves.map(move => move[
|
|
31875
|
-
const maxYMove = Math.min(...maxMoves.map(move => move[
|
|
31876
|
-
const minXMove = Math.max(...minMoves.map(move => move[
|
|
31877
|
-
const minYMove = Math.max(...minMoves.map(move => move[
|
|
32351
|
+
const maxXMove = Math.min(...maxMoves.map(move => move[X]));
|
|
32352
|
+
const maxYMove = Math.min(...maxMoves.map(move => move[Y]));
|
|
32353
|
+
const minXMove = Math.max(...minMoves.map(move => move[X]));
|
|
32354
|
+
const minYMove = Math.max(...minMoves.map(move => move[Y]));
|
|
31878
32355
|
const dx = clamp(deltaX, minXMove, maxXMove);
|
|
31879
32356
|
const dy = clamp(deltaY, minYMove, maxYMove);
|
|
31880
32357
|
return [dx, dy];
|
|
@@ -31905,7 +32382,122 @@ function boundAndSnapToGrid(point, {
|
|
|
31905
32382
|
point
|
|
31906
32383
|
}));
|
|
31907
32384
|
}
|
|
31908
|
-
function
|
|
32385
|
+
function boundAndSnapAngleVertex({
|
|
32386
|
+
range,
|
|
32387
|
+
coords,
|
|
32388
|
+
snapStep
|
|
32389
|
+
}, {
|
|
32390
|
+
destination
|
|
32391
|
+
}) {
|
|
32392
|
+
// Needed to prevent updating the original coords before the checks for
|
|
32393
|
+
// degenerate triangles and overlapping sides
|
|
32394
|
+
const coordsCopy = [...coords];
|
|
32395
|
+
|
|
32396
|
+
// Get the current and upcoming positions of the vertex
|
|
32397
|
+
const startingVertex = coordsCopy[1];
|
|
32398
|
+
const newVertex = bound$1({
|
|
32399
|
+
snapStep,
|
|
32400
|
+
range,
|
|
32401
|
+
point: snap(snapStep, destination)
|
|
32402
|
+
});
|
|
32403
|
+
|
|
32404
|
+
// Get the vector from the starting vertex to the new vertex
|
|
32405
|
+
const delta = vec.add(newVertex, reverseVector(startingVertex));
|
|
32406
|
+
|
|
32407
|
+
// Apply the delta to each of the other two points so that the angle is maintained
|
|
32408
|
+
let valid = true;
|
|
32409
|
+
const newPoints = {};
|
|
32410
|
+
for (const i of [0, 2]) {
|
|
32411
|
+
const oldPoint = coordsCopy[i];
|
|
32412
|
+
let newPoint = vec.add(oldPoint, delta);
|
|
32413
|
+
let angle = findAngle(newVertex, newPoint);
|
|
32414
|
+
angle *= Math.PI / 180;
|
|
32415
|
+
newPoint = constrainToBoundsOnAngle(newPoint, angle, range, snapStep);
|
|
32416
|
+
newPoints[i] = newPoint;
|
|
32417
|
+
|
|
32418
|
+
// Check if the new point is too close to the vertex
|
|
32419
|
+
if (tooClose(newVertex, newPoint)) {
|
|
32420
|
+
valid = false;
|
|
32421
|
+
}
|
|
32422
|
+
}
|
|
32423
|
+
|
|
32424
|
+
// Update the vertex after snapping to the snapStep
|
|
32425
|
+
newPoints[1] = newVertex;
|
|
32426
|
+
// Only move points if all new positions are valid
|
|
32427
|
+
if (valid) {
|
|
32428
|
+
Object.entries(newPoints).forEach(([i, newPoint]) => {
|
|
32429
|
+
coordsCopy[i] = newPoint;
|
|
32430
|
+
});
|
|
32431
|
+
}
|
|
32432
|
+
return coordsCopy;
|
|
32433
|
+
}
|
|
32434
|
+
|
|
32435
|
+
// This function is used to ensure that the vertex of
|
|
32436
|
+
// an angle is not too close to the other points
|
|
32437
|
+
function tooClose(point1, point2, range) {
|
|
32438
|
+
const safeDistance = 2;
|
|
32439
|
+
const distance = vec.dist(point1, point2);
|
|
32440
|
+
return distance < safeDistance;
|
|
32441
|
+
}
|
|
32442
|
+
function constrainToBoundsOnAngle(point, angle, range, snapStep) {
|
|
32443
|
+
// We're subtracting the snapStep from the lower bound and adding it to the upper bound
|
|
32444
|
+
// to ensure that the point is within the bounds of the graph even after snapping to the nearest degree
|
|
32445
|
+
const lower = [range[0][0] + snapStep[0], range[1][0] + snapStep[0]];
|
|
32446
|
+
const upper = [range[0][1] - snapStep[1], range[1][1] - snapStep[1]];
|
|
32447
|
+
let result = point;
|
|
32448
|
+
if (result[0] < lower[0]) {
|
|
32449
|
+
result = [lower[0], result[1] + (lower[0] - result[0]) * Math.tan(angle)];
|
|
32450
|
+
} else if (result[0] > upper[0]) {
|
|
32451
|
+
result = [upper[0], result[1] - (result[0] - upper[0]) * Math.tan(angle)];
|
|
32452
|
+
}
|
|
32453
|
+
if (result[1] < lower[1]) {
|
|
32454
|
+
result = [result[0] + (lower[1] - result[1]) / Math.tan(angle), lower[1]];
|
|
32455
|
+
} else if (result[1] > upper[1]) {
|
|
32456
|
+
result = [result[0] - (result[1] - upper[1]) / Math.tan(angle), upper[1]];
|
|
32457
|
+
}
|
|
32458
|
+
return result;
|
|
32459
|
+
}
|
|
32460
|
+
function boundAndSnapAngleEndPoints(destinationPoint, {
|
|
32461
|
+
range,
|
|
32462
|
+
coords,
|
|
32463
|
+
snapDegrees,
|
|
32464
|
+
angleOffsetDeg,
|
|
32465
|
+
snapStep
|
|
32466
|
+
}, index) {
|
|
32467
|
+
const snap = snapDegrees || 1;
|
|
32468
|
+
const offsetDegrees = angleOffsetDeg || 0;
|
|
32469
|
+
|
|
32470
|
+
// Needed to prevent updating the original coords before the checks for
|
|
32471
|
+
// degenerate triangles and overlapping sides
|
|
32472
|
+
const coordsCopy = [...coords];
|
|
32473
|
+
|
|
32474
|
+
// We want to subtract or add the snapStep to the lower and upper bounds
|
|
32475
|
+
// respectively to ensure that the point is within the bounds of the graph
|
|
32476
|
+
// even after snapping to the nearest degree
|
|
32477
|
+
const angleRange = [[range[0][0] + snapStep[0], range[0][1] - snapStep[0]], [range[1][0] + snapStep[1], range[1][1] - snapStep[1]]];
|
|
32478
|
+
|
|
32479
|
+
// Takes the destination point and makes sure it is within the bounds of the graph
|
|
32480
|
+
// SnapStep is [0, 0] because we don't want to snap these points to the grid at all
|
|
32481
|
+
const boundPoint = bound$1({
|
|
32482
|
+
snapStep: [0, 0],
|
|
32483
|
+
range: angleRange,
|
|
32484
|
+
point: destinationPoint
|
|
32485
|
+
});
|
|
32486
|
+
coordsCopy[index] = boundPoint;
|
|
32487
|
+
|
|
32488
|
+
// Get the vertex of the angle
|
|
32489
|
+
const vertex = coords[1];
|
|
32490
|
+
|
|
32491
|
+
// Gets the angle between the coords and the vertex
|
|
32492
|
+
let angle = findAngle(coordsCopy[index], vertex);
|
|
32493
|
+
|
|
32494
|
+
// Snap the angle to the nearest multiple of snapDegrees (if provided)
|
|
32495
|
+
angle = Math.round((angle - offsetDegrees) / snap) * snap + offsetDegrees;
|
|
32496
|
+
const distance = vec.dist(coordsCopy[index], vertex);
|
|
32497
|
+
const snappedValue = vec.add(vertex, polar(distance, angle));
|
|
32498
|
+
return snappedValue;
|
|
32499
|
+
}
|
|
32500
|
+
function boundAndSnapToPolygonAngle(destinationPoint, {
|
|
31909
32501
|
range,
|
|
31910
32502
|
coords
|
|
31911
32503
|
}, index) {
|
|
@@ -31936,7 +32528,7 @@ function boundAndSnapToAngle(destinationPoint, {
|
|
|
31936
32528
|
angles[rel(j)] = Math.round(angles[rel(j)]);
|
|
31937
32529
|
});
|
|
31938
32530
|
const getAngle = function getAngle(a, vertex, b) {
|
|
31939
|
-
const angle =
|
|
32531
|
+
const angle = findAngle(coordsCopy[rel(a)], coordsCopy[rel(b)], coordsCopy[rel(vertex)]);
|
|
31940
32532
|
return (angle + 360) % 360;
|
|
31941
32533
|
};
|
|
31942
32534
|
const innerAngles = [angles[rel(-1)] - getAngle(-2, -1, 1), angles[rel(1)] - getAngle(-1, 1, 2)];
|
|
@@ -31964,7 +32556,7 @@ function boundAndSnapToAngle(destinationPoint, {
|
|
|
31964
32556
|
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
31965
32557
|
|
|
31966
32558
|
// Angle at the second vertex of the polygon
|
|
31967
|
-
const outerAngle =
|
|
32559
|
+
const outerAngle = findAngle(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
31968
32560
|
|
|
31969
32561
|
// Uses the length of the side of the polygon (radial coordinate)
|
|
31970
32562
|
// and the angle between the first and second sides of the
|
|
@@ -32014,7 +32606,7 @@ function boundAndSnapToSides(destinationPoint, {
|
|
|
32014
32606
|
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
32015
32607
|
|
|
32016
32608
|
// Angle at the second vertex of the polygon
|
|
32017
|
-
const outerAngle =
|
|
32609
|
+
const outerAngle = findAngle(coordsCopy[rel(1)], coordsCopy[rel(-1)]);
|
|
32018
32610
|
|
|
32019
32611
|
// Returns true if the points form a counter-clockwise turn;
|
|
32020
32612
|
// a.k.a if the point is on the left or right of the polygon.
|
|
@@ -32083,65 +32675,6 @@ function setAtIndex(args) {
|
|
|
32083
32675
|
return copy;
|
|
32084
32676
|
}
|
|
32085
32677
|
|
|
32086
|
-
const renderGraph = props => {
|
|
32087
|
-
const {
|
|
32088
|
-
state,
|
|
32089
|
-
dispatch
|
|
32090
|
-
} = props;
|
|
32091
|
-
const {
|
|
32092
|
-
type
|
|
32093
|
-
} = state;
|
|
32094
|
-
switch (type) {
|
|
32095
|
-
case "segment":
|
|
32096
|
-
return /*#__PURE__*/React.createElement(SegmentGraph, {
|
|
32097
|
-
graphState: state,
|
|
32098
|
-
dispatch: dispatch
|
|
32099
|
-
});
|
|
32100
|
-
case "linear-system":
|
|
32101
|
-
return /*#__PURE__*/React.createElement(LinearSystemGraph, {
|
|
32102
|
-
graphState: state,
|
|
32103
|
-
dispatch: dispatch
|
|
32104
|
-
});
|
|
32105
|
-
case "linear":
|
|
32106
|
-
return /*#__PURE__*/React.createElement(LinearGraph, {
|
|
32107
|
-
graphState: state,
|
|
32108
|
-
dispatch: dispatch
|
|
32109
|
-
});
|
|
32110
|
-
case "ray":
|
|
32111
|
-
return /*#__PURE__*/React.createElement(RayGraph, {
|
|
32112
|
-
graphState: state,
|
|
32113
|
-
dispatch: dispatch
|
|
32114
|
-
});
|
|
32115
|
-
case "polygon":
|
|
32116
|
-
return /*#__PURE__*/React.createElement(PolygonGraph, {
|
|
32117
|
-
graphState: state,
|
|
32118
|
-
dispatch: dispatch
|
|
32119
|
-
});
|
|
32120
|
-
case "point":
|
|
32121
|
-
return /*#__PURE__*/React.createElement(PointGraph, {
|
|
32122
|
-
graphState: state,
|
|
32123
|
-
dispatch: dispatch
|
|
32124
|
-
});
|
|
32125
|
-
case "circle":
|
|
32126
|
-
return /*#__PURE__*/React.createElement(CircleGraph, {
|
|
32127
|
-
graphState: state,
|
|
32128
|
-
dispatch: dispatch
|
|
32129
|
-
});
|
|
32130
|
-
case "quadratic":
|
|
32131
|
-
return /*#__PURE__*/React.createElement(QuadraticGraph, {
|
|
32132
|
-
graphState: state,
|
|
32133
|
-
dispatch: dispatch
|
|
32134
|
-
});
|
|
32135
|
-
case "sinusoid":
|
|
32136
|
-
return /*#__PURE__*/React.createElement(SinusoidGraph, {
|
|
32137
|
-
graphState: state,
|
|
32138
|
-
dispatch: dispatch
|
|
32139
|
-
});
|
|
32140
|
-
default:
|
|
32141
|
-
return new UnreachableCaseError(type);
|
|
32142
|
-
}
|
|
32143
|
-
};
|
|
32144
|
-
|
|
32145
32678
|
// Rather than be tightly bound to how data was structured in
|
|
32146
32679
|
// the legacy interactive graph, this lets us store state
|
|
32147
32680
|
// however we want and we just transform it before handing it off
|
|
@@ -32207,72 +32740,6 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
32207
32740
|
dispatch: dispatch
|
|
32208
32741
|
}));
|
|
32209
32742
|
});
|
|
32210
|
-
const MafsGraph = props => {
|
|
32211
|
-
const {
|
|
32212
|
-
state,
|
|
32213
|
-
dispatch,
|
|
32214
|
-
labels
|
|
32215
|
-
} = props;
|
|
32216
|
-
const [width, height] = props.box;
|
|
32217
|
-
const tickStep = props.step;
|
|
32218
|
-
return /*#__PURE__*/React.createElement(GraphConfigContext.Provider, {
|
|
32219
|
-
value: {
|
|
32220
|
-
range: state.range,
|
|
32221
|
-
snapStep: state.snapStep,
|
|
32222
|
-
markings: props.markings,
|
|
32223
|
-
tickStep: tickStep,
|
|
32224
|
-
gridStep: props.gridStep,
|
|
32225
|
-
showTooltips: !!props.showTooltips,
|
|
32226
|
-
graphDimensionsInPixels: props.box,
|
|
32227
|
-
width,
|
|
32228
|
-
height,
|
|
32229
|
-
labels
|
|
32230
|
-
}
|
|
32231
|
-
}, /*#__PURE__*/React.createElement(View, {
|
|
32232
|
-
className: "mafs-graph",
|
|
32233
|
-
style: {
|
|
32234
|
-
width,
|
|
32235
|
-
height,
|
|
32236
|
-
position: "relative",
|
|
32237
|
-
padding: "25px 25px 0 0",
|
|
32238
|
-
boxSizing: "content-box",
|
|
32239
|
-
marginLeft: "20px",
|
|
32240
|
-
marginBottom: "20px"
|
|
32241
|
-
}
|
|
32242
|
-
}, /*#__PURE__*/React.createElement(LegacyGrid, {
|
|
32243
|
-
box: props.box,
|
|
32244
|
-
backgroundImage: props.backgroundImage
|
|
32245
|
-
}), /*#__PURE__*/React.createElement(View, {
|
|
32246
|
-
style: {
|
|
32247
|
-
position: "absolute",
|
|
32248
|
-
bottom: 0,
|
|
32249
|
-
left: 0
|
|
32250
|
-
}
|
|
32251
|
-
}, props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisLabels, null), /*#__PURE__*/React.createElement(AxisTickLabels, null)), /*#__PURE__*/React.createElement(Mafs, {
|
|
32252
|
-
preserveAspectRatio: false,
|
|
32253
|
-
viewBox: {
|
|
32254
|
-
x: state.range[0],
|
|
32255
|
-
y: state.range[1],
|
|
32256
|
-
padding: 0
|
|
32257
|
-
},
|
|
32258
|
-
pan: false,
|
|
32259
|
-
zoom: false,
|
|
32260
|
-
width: width,
|
|
32261
|
-
height: height
|
|
32262
|
-
}, /*#__PURE__*/React.createElement(SvgDefs, null), /*#__PURE__*/React.createElement(Grid, {
|
|
32263
|
-
tickStep: props.step,
|
|
32264
|
-
gridStep: props.gridStep,
|
|
32265
|
-
range: state.range,
|
|
32266
|
-
containerSizeClass: props.containerSizeClass,
|
|
32267
|
-
markings: props.markings
|
|
32268
|
-
}), props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
|
|
32269
|
-
lockedFigures: props.lockedFigures,
|
|
32270
|
-
range: state.range
|
|
32271
|
-
}), "/* Protractor */", props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
|
|
32272
|
-
state,
|
|
32273
|
-
dispatch
|
|
32274
|
-
})))));
|
|
32275
|
-
};
|
|
32276
32743
|
|
|
32277
32744
|
const {
|
|
32278
32745
|
DeprecationMixin
|
|
@@ -33093,7 +33560,7 @@ class LegacyInteractiveGraph extends React.Component {
|
|
|
33093
33560
|
// Solve for side by using the law of sines
|
|
33094
33561
|
const side = Math.sin(innerAngles[1] * Math.PI / 180) / Math.sin(innerAngles[2] * Math.PI / 180) * knownSide;
|
|
33095
33562
|
const outerAngle = GraphUtils.findAngle(coords[rel(1)], coords[rel(-1)]);
|
|
33096
|
-
const offset = polar(side, outerAngle + (onLeft ? 1 : -1) * innerAngles[0]);
|
|
33563
|
+
const offset = polar$1(side, outerAngle + (onLeft ? 1 : -1) * innerAngles[0]);
|
|
33097
33564
|
return (_this$graphie5 = this.graphie) == null ? void 0 : _this$graphie5.addPoints(coords[rel(-1)], offset);
|
|
33098
33565
|
}
|
|
33099
33566
|
// @ts-expect-error - TS2339 - Property 'snapTo' does not exist on type 'PerseusGraphType'.
|
|
@@ -33118,7 +33585,7 @@ class LegacyInteractiveGraph extends React.Component {
|
|
|
33118
33585
|
const innerAngle = lawOfCosines(sides[0], sides[2], sides[1]);
|
|
33119
33586
|
const outerAngle = GraphUtils.findAngle(coords[rel(1)], coords[rel(-1)]);
|
|
33120
33587
|
const onLeft = sign(ccw(coords[rel(-1)], coords[rel(1)], coords[i])) === 1;
|
|
33121
|
-
const offset = polar(sides[0], outerAngle + (onLeft ? 1 : -1) * innerAngle);
|
|
33588
|
+
const offset = polar$1(sides[0], outerAngle + (onLeft ? 1 : -1) * innerAngle);
|
|
33122
33589
|
return (_this$graphie6 = this.graphie) == null ? void 0 : _this$graphie6.addPoints(coords[rel(-1)], offset);
|
|
33123
33590
|
}
|
|
33124
33591
|
// Snap to grid (already done)
|
|
@@ -33779,11 +34246,9 @@ class InteractiveGraph extends React.Component {
|
|
|
33779
34246
|
const radius = magnitude(vector(...coords));
|
|
33780
34247
|
|
|
33781
34248
|
// Adjust the lower point by angleOffsetDeg degrees
|
|
33782
|
-
// @ts-expect-error - TS2542 - Index signature in type 'readonly Coord[]' only permits reading.
|
|
33783
34249
|
coords[0] = [coords[1][0] + radius * Math.cos(offset), coords[1][1] + radius * Math.sin(offset)];
|
|
33784
34250
|
// Position the upper point angle radians from the
|
|
33785
34251
|
// lower point
|
|
33786
|
-
// @ts-expect-error - TS2542 - Index signature in type 'readonly Coord[]' only permits reading.
|
|
33787
34252
|
coords[2] = [coords[1][0] + radius * Math.cos(angle + offset), coords[1][1] + radius * Math.sin(angle + offset)];
|
|
33788
34253
|
return coords;
|
|
33789
34254
|
}
|
|
@@ -41341,6 +41806,7 @@ const rules = {
|
|
|
41341
41806
|
}, /*#__PURE__*/React.createElement("span", {
|
|
41342
41807
|
style: SQUARE_LABEL_STYLE
|
|
41343
41808
|
}, node.content)), /*#__PURE__*/React.createElement(AltText, {
|
|
41809
|
+
key: "alt-text",
|
|
41344
41810
|
id: "questionMarker",
|
|
41345
41811
|
number: node.content
|
|
41346
41812
|
}), node.space ? "\u00A0" : null];
|
|
@@ -41365,6 +41831,7 @@ const rules = {
|
|
|
41365
41831
|
}, /*#__PURE__*/React.createElement("span", {
|
|
41366
41832
|
style: CIRCLE_LABEL_STYLE
|
|
41367
41833
|
}, node.content)), /*#__PURE__*/React.createElement(AltText, {
|
|
41834
|
+
key: "alt-text",
|
|
41368
41835
|
id: "circleMarker",
|
|
41369
41836
|
number: node.content
|
|
41370
41837
|
}), node.space ? "\u00A0" : null];
|
|
@@ -41388,6 +41855,7 @@ const rules = {
|
|
|
41388
41855
|
className: "perseus-passage-bracket-label",
|
|
41389
41856
|
"aria-hidden": "true"
|
|
41390
41857
|
}, "[", node.content, "]"), /*#__PURE__*/React.createElement(AltText, {
|
|
41858
|
+
key: "alt-text",
|
|
41391
41859
|
id: "sentenceMarker",
|
|
41392
41860
|
number: node.content
|
|
41393
41861
|
}), node.space ? "\u00A0" : null];
|
|
@@ -44396,7 +44864,7 @@ const init = function init(options) {
|
|
|
44396
44864
|
|
|
44397
44865
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44398
44866
|
const libName = "@khanacademy/perseus";
|
|
44399
|
-
const libVersion = "
|
|
44867
|
+
const libVersion = "25.0.0";
|
|
44400
44868
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44401
44869
|
|
|
44402
44870
|
const apiVersion = {
|
|
@@ -47397,5 +47865,5 @@ const EditorJsonify = {
|
|
|
47397
47865
|
}
|
|
47398
47866
|
};
|
|
47399
47867
|
|
|
47400
|
-
export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer,
|
|
47868
|
+
export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, 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, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAnswerFromUserInput, getAnswersFromWidgets, getCorrectAnswerForWidgetId, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
47401
47869
|
//# sourceMappingURL=index.js.map
|