@khanacademy/perseus 23.0.0 → 23.3.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.js +341 -105
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +356 -108
- package/dist/index.js.map +1 -1
- package/dist/perseus-api.d.ts +2 -1
- package/dist/perseus-types.d.ts +20 -4
- package/dist/server-item-renderer.d.ts +2 -1
- package/dist/types.d.ts +2 -2
- package/dist/util/geometry.d.ts +1 -0
- package/dist/widgets/expression.d.ts +12 -6
- package/dist/widgets/interactive-graphs/graphs/components/css-cursor.d.ts +1 -0
- package/dist/widgets/interactive-graphs/graphs/components/movable-point-view.d.ts +2 -0
- package/dist/widgets/interactive-graphs/graphs/components/movable-point.d.ts +2 -0
- package/dist/widgets/interactive-graphs/graphs/index.d.ts +2 -0
- package/dist/widgets/interactive-graphs/graphs/linear-system.d.ts +5 -0
- package/dist/widgets/interactive-graphs/graphs/linear.d.ts +1 -1
- package/dist/widgets/interactive-graphs/graphs/sinusoid.d.ts +14 -0
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +8 -1
- package/dist/widgets/interactive-graphs/locked-ellipse.d.ts +4 -0
- package/dist/widgets/interactive-graphs/locked-vector.d.ts +4 -0
- package/dist/widgets/interactive-graphs/mafs-supported-graph-types.d.ts +1 -1
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-state.d.ts +1 -1
- package/dist/widgets/interactive-graphs/types.d.ts +12 -9
- package/dist/widgets/label-image.d.ts +4 -2
- package/package.json +1 -1
package/dist/es/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import { OptionItem, SingleSelect, MultiSelect } from '@khanacademy/wonder-block
|
|
|
29
29
|
import caretDown from '@phosphor-icons/core/regular/caret-down.svg';
|
|
30
30
|
import caretUp from '@phosphor-icons/core/regular/caret-up.svg';
|
|
31
31
|
import debounce$2 from 'lodash.debounce';
|
|
32
|
-
import { vec, Line as Line$4, Point as Point$2, useMovable, Text as Text$1, Polygon, Plot as Plot$3, Coordinates, Mafs } from 'mafs';
|
|
32
|
+
import { vec, Ellipse, Line as Line$4, Point as Point$2, useMovable, Text as Text$1, Polygon, Plot as Plot$3, Coordinates, Mafs } from 'mafs';
|
|
33
33
|
import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
|
|
34
34
|
import { Body, LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
|
|
35
35
|
import questionIcon from '@phosphor-icons/core/regular/question.svg';
|
|
@@ -10827,6 +10827,33 @@ function intersects(ab, cd) {
|
|
|
10827
10827
|
}
|
|
10828
10828
|
return false;
|
|
10829
10829
|
}
|
|
10830
|
+
|
|
10831
|
+
// Whether any two sides of a polygon intersect each other
|
|
10832
|
+
function polygonSidesIntersect(vertices) {
|
|
10833
|
+
for (let i = 0; i < vertices.length; i++) {
|
|
10834
|
+
for (let k = i + 1; k < vertices.length; k++) {
|
|
10835
|
+
// If any two vertices are the same point, sides overlap
|
|
10836
|
+
if (point.equal(vertices[i], vertices[k])) {
|
|
10837
|
+
return true;
|
|
10838
|
+
}
|
|
10839
|
+
|
|
10840
|
+
// Find the other end of the sides starting at vertices i and k
|
|
10841
|
+
const iNext = (i + 1) % vertices.length;
|
|
10842
|
+
const kNext = (k + 1) % vertices.length;
|
|
10843
|
+
|
|
10844
|
+
// Adjacent sides always intersect (at the vertex); skip those
|
|
10845
|
+
if (iNext === k || kNext === i) {
|
|
10846
|
+
continue;
|
|
10847
|
+
}
|
|
10848
|
+
const side1 = [vertices[i], vertices[iNext]];
|
|
10849
|
+
const side2 = [vertices[k], vertices[kNext]];
|
|
10850
|
+
if (intersects(side1, side2)) {
|
|
10851
|
+
return true;
|
|
10852
|
+
}
|
|
10853
|
+
}
|
|
10854
|
+
}
|
|
10855
|
+
return false;
|
|
10856
|
+
}
|
|
10830
10857
|
function vector(a, b) {
|
|
10831
10858
|
return _.map(_.zip(a, b), function (pair) {
|
|
10832
10859
|
return pair[0] - pair[1];
|
|
@@ -17430,16 +17457,8 @@ class SvgImage extends React.Component {
|
|
|
17430
17457
|
// Props to send to all images
|
|
17431
17458
|
const imageProps = {
|
|
17432
17459
|
alt: this.props.alt,
|
|
17433
|
-
title: this.props.title
|
|
17434
|
-
tabIndex: "0"
|
|
17460
|
+
title: this.props.title
|
|
17435
17461
|
};
|
|
17436
|
-
// If alt text is present, AND overrideAriaHidden is true,
|
|
17437
|
-
// include aria-hidden to avoid screen reader from crawling texts
|
|
17438
|
-
// both from the div meant for screen readers (see svg-image.jsx)
|
|
17439
|
-
// and alt attribute.
|
|
17440
|
-
if (this.props.alt && this.props.overrideAriaHidden) {
|
|
17441
|
-
imageProps["aria-hidden"] = true;
|
|
17442
|
-
}
|
|
17443
17462
|
const width = this.props.width && this.props.width * this.props.scale;
|
|
17444
17463
|
const height = this.props.height && this.props.height * this.props.scale;
|
|
17445
17464
|
const dimensions = {
|
|
@@ -27361,7 +27380,7 @@ class ImageWidget extends React.Component {
|
|
|
27361
27380
|
|
|
27362
27381
|
render() {
|
|
27363
27382
|
let image;
|
|
27364
|
-
|
|
27383
|
+
const alt = this.props.caption === this.props.alt ? "" : this.props.alt;
|
|
27365
27384
|
const {
|
|
27366
27385
|
apiOptions
|
|
27367
27386
|
} = this.props;
|
|
@@ -27372,29 +27391,7 @@ class ImageWidget extends React.Component {
|
|
|
27372
27391
|
setAssetStatus
|
|
27373
27392
|
}) => /*#__PURE__*/React.createElement(SvgImage, {
|
|
27374
27393
|
src: url,
|
|
27375
|
-
alt:
|
|
27376
|
-
/* alt text is formatted in a sr-only
|
|
27377
|
-
div next to the image in addition to
|
|
27378
|
-
the alt attribute.
|
|
27379
|
-
If there is no alt text at all,
|
|
27380
|
-
we don't put an alt attribute on
|
|
27381
|
-
the image, so that screen readers
|
|
27382
|
-
know there's something they can't
|
|
27383
|
-
read there :(.
|
|
27384
|
-
NOTE: React <=0.13 (maybe later)
|
|
27385
|
-
has a bug where it won't ever
|
|
27386
|
-
remove an attribute, so if this
|
|
27387
|
-
alt node is ever defined it's
|
|
27388
|
-
not removed. This is sort of
|
|
27389
|
-
dangerous, but we usually re-key
|
|
27390
|
-
new renderers so that they're
|
|
27391
|
-
rendered from scratch anyways,
|
|
27392
|
-
so this shouldn't be a problem
|
|
27393
|
-
in practice right now, although
|
|
27394
|
-
it will exhibit weird behaviour
|
|
27395
|
-
while editing. */
|
|
27396
|
-
this.props.alt,
|
|
27397
|
-
overrideAriaHidden: true,
|
|
27394
|
+
alt: alt,
|
|
27398
27395
|
width: backgroundImage.width,
|
|
27399
27396
|
height: backgroundImage.height,
|
|
27400
27397
|
preloader: apiOptions.imagePreloader,
|
|
@@ -27410,16 +27407,6 @@ class ImageWidget extends React.Component {
|
|
|
27410
27407
|
setAssetStatus: setAssetStatus
|
|
27411
27408
|
}));
|
|
27412
27409
|
}
|
|
27413
|
-
if (this.props.alt) {
|
|
27414
|
-
alt = /*#__PURE__*/React.createElement("span", {
|
|
27415
|
-
className: "perseus-sr-only"
|
|
27416
|
-
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
27417
|
-
content: this.props.alt,
|
|
27418
|
-
apiOptions: apiOptions,
|
|
27419
|
-
linterContext: this.props.linterContext,
|
|
27420
|
-
strings: this.context.strings
|
|
27421
|
-
}));
|
|
27422
|
-
}
|
|
27423
27410
|
|
|
27424
27411
|
// For mobile we combine an image's title and caption.
|
|
27425
27412
|
if (apiOptions.isMobile) {
|
|
@@ -27486,7 +27473,7 @@ class ImageWidget extends React.Component {
|
|
|
27486
27473
|
style: {
|
|
27487
27474
|
maxWidth: backgroundImage.width
|
|
27488
27475
|
}
|
|
27489
|
-
}, image,
|
|
27476
|
+
}, image, titleAndCaption);
|
|
27490
27477
|
}
|
|
27491
27478
|
let title;
|
|
27492
27479
|
let caption;
|
|
@@ -27518,7 +27505,7 @@ class ImageWidget extends React.Component {
|
|
|
27518
27505
|
style: {
|
|
27519
27506
|
maxWidth: backgroundImage.width
|
|
27520
27507
|
}
|
|
27521
|
-
}, title, image,
|
|
27508
|
+
}, title, image, caption);
|
|
27522
27509
|
}
|
|
27523
27510
|
static validate(userInput, rubric) {
|
|
27524
27511
|
return {
|
|
@@ -28671,8 +28658,32 @@ const lockedFigureColors = {
|
|
|
28671
28658
|
pink: "#B25071",
|
|
28672
28659
|
red: "#D92916"
|
|
28673
28660
|
};
|
|
28661
|
+
const lockedEllipseFillStyles = {
|
|
28662
|
+
none: 0,
|
|
28663
|
+
solid: 1,
|
|
28664
|
+
translucent: 0.4
|
|
28665
|
+
};
|
|
28674
28666
|
const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
|
|
28675
28667
|
|
|
28668
|
+
const LockedEllipse = props => {
|
|
28669
|
+
const {
|
|
28670
|
+
center,
|
|
28671
|
+
radius,
|
|
28672
|
+
angle,
|
|
28673
|
+
color,
|
|
28674
|
+
fillStyle,
|
|
28675
|
+
strokeStyle
|
|
28676
|
+
} = props;
|
|
28677
|
+
return /*#__PURE__*/React.createElement(Ellipse, {
|
|
28678
|
+
center: center,
|
|
28679
|
+
radius: radius,
|
|
28680
|
+
angle: angle,
|
|
28681
|
+
fillOpacity: lockedEllipseFillStyles[fillStyle],
|
|
28682
|
+
strokeStyle: strokeStyle,
|
|
28683
|
+
color: lockedFigureColors[color]
|
|
28684
|
+
});
|
|
28685
|
+
};
|
|
28686
|
+
|
|
28676
28687
|
function pathBuilder() {
|
|
28677
28688
|
return new PathBuilder();
|
|
28678
28689
|
}
|
|
@@ -28927,6 +28938,21 @@ const LockedPoint = props => {
|
|
|
28927
28938
|
});
|
|
28928
28939
|
};
|
|
28929
28940
|
|
|
28941
|
+
const LockedVector = props => {
|
|
28942
|
+
const {
|
|
28943
|
+
color,
|
|
28944
|
+
points
|
|
28945
|
+
} = props;
|
|
28946
|
+
const [tail, tip] = points;
|
|
28947
|
+
return /*#__PURE__*/React.createElement("g", {
|
|
28948
|
+
className: "locked-vector"
|
|
28949
|
+
}, /*#__PURE__*/React.createElement(Vector, {
|
|
28950
|
+
tail: tail,
|
|
28951
|
+
tip: tip,
|
|
28952
|
+
color: lockedFigureColors[color]
|
|
28953
|
+
}));
|
|
28954
|
+
};
|
|
28955
|
+
|
|
28930
28956
|
const GraphLockedLayer = props => {
|
|
28931
28957
|
const {
|
|
28932
28958
|
lockedFigures
|
|
@@ -28942,6 +28968,14 @@ const GraphLockedLayer = props => {
|
|
|
28942
28968
|
key: `line-${index}`,
|
|
28943
28969
|
range: props.range
|
|
28944
28970
|
}, figure));
|
|
28971
|
+
case "ellipse":
|
|
28972
|
+
return /*#__PURE__*/React.createElement(LockedEllipse, _extends({
|
|
28973
|
+
key: `ellipse-${index}`
|
|
28974
|
+
}, figure));
|
|
28975
|
+
case "vector":
|
|
28976
|
+
return /*#__PURE__*/React.createElement(LockedVector, _extends({
|
|
28977
|
+
key: `vector-${index}`
|
|
28978
|
+
}, figure));
|
|
28945
28979
|
default:
|
|
28946
28980
|
/**
|
|
28947
28981
|
* Devlopment-time future-proofing: This should
|
|
@@ -29070,7 +29104,8 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
|
|
|
29070
29104
|
point,
|
|
29071
29105
|
color: color$1 = color.blue,
|
|
29072
29106
|
dragging,
|
|
29073
|
-
focusBehavior
|
|
29107
|
+
focusBehavior,
|
|
29108
|
+
cursor
|
|
29074
29109
|
} = props;
|
|
29075
29110
|
|
|
29076
29111
|
// WB Tooltip requires a color name for the background color.
|
|
@@ -29102,7 +29137,8 @@ const MovablePointView = /*#__PURE__*/forwardRef((props, hitboxRef) => {
|
|
|
29102
29137
|
ref: hitboxRef,
|
|
29103
29138
|
className: pointClasses,
|
|
29104
29139
|
style: {
|
|
29105
|
-
"--movable-point-color": color$1
|
|
29140
|
+
"--movable-point-color": color$1,
|
|
29141
|
+
cursor
|
|
29106
29142
|
},
|
|
29107
29143
|
"data-testid": "movable-point",
|
|
29108
29144
|
tabIndex: tabIndex(focusBehavior)
|
|
@@ -29350,7 +29386,33 @@ const SegmentGraph = props => {
|
|
|
29350
29386
|
})));
|
|
29351
29387
|
};
|
|
29352
29388
|
|
|
29353
|
-
const LinearGraph = props => {
|
|
29389
|
+
const LinearGraph = (props, key) => {
|
|
29390
|
+
const {
|
|
29391
|
+
dispatch
|
|
29392
|
+
} = props;
|
|
29393
|
+
const {
|
|
29394
|
+
coords: line
|
|
29395
|
+
} = props.graphState;
|
|
29396
|
+
|
|
29397
|
+
// Linear graphs only have one line
|
|
29398
|
+
// (LEMS-2050): Update the reducer so that we have a separate action for moving one line
|
|
29399
|
+
// and another action for moving multiple lines
|
|
29400
|
+
return /*#__PURE__*/React.createElement(MovableLine, {
|
|
29401
|
+
key: 0,
|
|
29402
|
+
points: line,
|
|
29403
|
+
onMoveLine: delta => {
|
|
29404
|
+
dispatch(moveLine(0, delta));
|
|
29405
|
+
},
|
|
29406
|
+
extend: {
|
|
29407
|
+
start: true,
|
|
29408
|
+
end: true
|
|
29409
|
+
},
|
|
29410
|
+
onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, 0)),
|
|
29411
|
+
color: "var(--movable-line-stroke-color)"
|
|
29412
|
+
});
|
|
29413
|
+
};
|
|
29414
|
+
|
|
29415
|
+
const LinearSystemGraph = props => {
|
|
29354
29416
|
const {
|
|
29355
29417
|
dispatch
|
|
29356
29418
|
} = props;
|
|
@@ -29370,7 +29432,7 @@ const LinearGraph = props => {
|
|
|
29370
29432
|
},
|
|
29371
29433
|
onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i)),
|
|
29372
29434
|
color: colors[i]
|
|
29373
|
-
})));
|
|
29435
|
+
})), ";");
|
|
29374
29436
|
};
|
|
29375
29437
|
|
|
29376
29438
|
const RayGraph = props => {
|
|
@@ -29378,14 +29440,14 @@ const RayGraph = props => {
|
|
|
29378
29440
|
dispatch
|
|
29379
29441
|
} = props;
|
|
29380
29442
|
const {
|
|
29381
|
-
coords:
|
|
29443
|
+
coords: line
|
|
29382
29444
|
} = props.graphState;
|
|
29383
29445
|
const handleMoveLine = delta => dispatch(moveLine(0, delta));
|
|
29384
29446
|
const handleMovePoint = (pointIndex, newPoint) => dispatch(moveControlPoint(pointIndex, newPoint, 0));
|
|
29385
|
-
|
|
29386
|
-
//
|
|
29387
|
-
, {
|
|
29388
|
-
points:
|
|
29447
|
+
|
|
29448
|
+
// Ray graphs only have one line
|
|
29449
|
+
return /*#__PURE__*/React.createElement(MovableLine, {
|
|
29450
|
+
points: line,
|
|
29389
29451
|
onMoveLine: handleMoveLine,
|
|
29390
29452
|
onMovePoint: handleMovePoint,
|
|
29391
29453
|
extend: {
|
|
@@ -29564,6 +29626,7 @@ const StyledMovablePoint = props => {
|
|
|
29564
29626
|
const {
|
|
29565
29627
|
point,
|
|
29566
29628
|
onMove,
|
|
29629
|
+
cursor,
|
|
29567
29630
|
color: color$1 = color.blue
|
|
29568
29631
|
} = props;
|
|
29569
29632
|
const {
|
|
@@ -29582,7 +29645,8 @@ const StyledMovablePoint = props => {
|
|
|
29582
29645
|
focusBehavior: {
|
|
29583
29646
|
type: "uncontrolled",
|
|
29584
29647
|
tabIndex: 0
|
|
29585
|
-
}
|
|
29648
|
+
},
|
|
29649
|
+
cursor: cursor
|
|
29586
29650
|
});
|
|
29587
29651
|
};
|
|
29588
29652
|
|
|
@@ -29686,25 +29750,39 @@ function initializeGraphState(params) {
|
|
|
29686
29750
|
case "segment":
|
|
29687
29751
|
return _extends({}, shared, {
|
|
29688
29752
|
type: "segment",
|
|
29689
|
-
coords:
|
|
29753
|
+
coords: getSegmentCoords({
|
|
29690
29754
|
graph,
|
|
29691
29755
|
step,
|
|
29692
29756
|
range
|
|
29693
29757
|
})
|
|
29694
29758
|
});
|
|
29695
29759
|
case "linear":
|
|
29696
|
-
|
|
29760
|
+
return _extends({}, shared, {
|
|
29761
|
+
type: graph.type,
|
|
29762
|
+
coords: getLineCoords({
|
|
29763
|
+
graph,
|
|
29764
|
+
range,
|
|
29765
|
+
step
|
|
29766
|
+
})
|
|
29767
|
+
});
|
|
29697
29768
|
case "ray":
|
|
29698
29769
|
return _extends({}, shared, {
|
|
29699
29770
|
type: graph.type,
|
|
29700
|
-
// Linear and ray graphs have a single tuple of points, while a
|
|
29701
|
-
// linear system has two tuples of points.
|
|
29702
29771
|
coords: getLineCoords({
|
|
29703
29772
|
graph,
|
|
29704
29773
|
range,
|
|
29705
29774
|
step
|
|
29706
29775
|
})
|
|
29707
29776
|
});
|
|
29777
|
+
case "linear-system":
|
|
29778
|
+
return _extends({}, shared, {
|
|
29779
|
+
type: graph.type,
|
|
29780
|
+
coords: getLinearSystemCoords({
|
|
29781
|
+
graph,
|
|
29782
|
+
range,
|
|
29783
|
+
step
|
|
29784
|
+
})
|
|
29785
|
+
});
|
|
29708
29786
|
case "polygon":
|
|
29709
29787
|
return _extends({}, shared, {
|
|
29710
29788
|
type: "polygon",
|
|
@@ -29727,17 +29805,19 @@ function initializeGraphState(params) {
|
|
|
29727
29805
|
});
|
|
29728
29806
|
case "circle":
|
|
29729
29807
|
return _extends({}, shared, {
|
|
29730
|
-
type: graph.type
|
|
29731
|
-
|
|
29732
|
-
radiusPoint: [1, 0]
|
|
29733
|
-
});
|
|
29808
|
+
type: graph.type
|
|
29809
|
+
}, getCircleCoords(graph));
|
|
29734
29810
|
case "quadratic":
|
|
29735
29811
|
return _extends({}, shared, {
|
|
29736
29812
|
type: graph.type,
|
|
29737
29813
|
coords: getQuadraticCoords(graph, range, step)
|
|
29738
29814
|
});
|
|
29739
|
-
case "angle":
|
|
29740
29815
|
case "sinusoid":
|
|
29816
|
+
return _extends({}, shared, {
|
|
29817
|
+
type: graph.type,
|
|
29818
|
+
coords: getSinusoidCoords(graph, range, step)
|
|
29819
|
+
});
|
|
29820
|
+
case "angle":
|
|
29741
29821
|
throw new Error("Mafs not yet implemented for graph type: " + graph.type);
|
|
29742
29822
|
default:
|
|
29743
29823
|
throw new UnreachableCaseError(graph);
|
|
@@ -29802,12 +29882,12 @@ function getGradableGraph(state, initialGraph) {
|
|
|
29802
29882
|
}
|
|
29803
29883
|
if (state.type === "linear" && initialGraph.type === "linear") {
|
|
29804
29884
|
return _extends({}, initialGraph, {
|
|
29805
|
-
coords: state.coords
|
|
29885
|
+
coords: state.coords
|
|
29806
29886
|
});
|
|
29807
29887
|
}
|
|
29808
29888
|
if (state.type === "ray" && initialGraph.type === "ray") {
|
|
29809
29889
|
return _extends({}, initialGraph, {
|
|
29810
|
-
coords: state.coords
|
|
29890
|
+
coords: state.coords
|
|
29811
29891
|
});
|
|
29812
29892
|
}
|
|
29813
29893
|
if (state.type === "polygon" && initialGraph.type === "polygon") {
|
|
@@ -29831,13 +29911,21 @@ function getGradableGraph(state, initialGraph) {
|
|
|
29831
29911
|
coords: state.coords
|
|
29832
29912
|
});
|
|
29833
29913
|
}
|
|
29914
|
+
if (state.type === "sinusoid" && initialGraph.type === "sinusoid") {
|
|
29915
|
+
return _extends({}, initialGraph, {
|
|
29916
|
+
coords: state.coords
|
|
29917
|
+
});
|
|
29918
|
+
}
|
|
29834
29919
|
throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
|
|
29835
29920
|
}
|
|
29836
|
-
const
|
|
29921
|
+
const getSegmentCoords = ({
|
|
29837
29922
|
graph,
|
|
29838
29923
|
range,
|
|
29839
29924
|
step
|
|
29840
29925
|
}) => {
|
|
29926
|
+
if (graph.coords) {
|
|
29927
|
+
return graph.coords;
|
|
29928
|
+
}
|
|
29841
29929
|
const ys = n => {
|
|
29842
29930
|
switch (n) {
|
|
29843
29931
|
case 2:
|
|
@@ -29868,16 +29956,20 @@ const getLineCoords = ({
|
|
|
29868
29956
|
range,
|
|
29869
29957
|
step
|
|
29870
29958
|
}) => {
|
|
29871
|
-
|
|
29872
|
-
|
|
29873
|
-
|
|
29874
|
-
|
|
29875
|
-
|
|
29876
|
-
|
|
29877
|
-
|
|
29878
|
-
|
|
29879
|
-
|
|
29959
|
+
if (graph.coords) {
|
|
29960
|
+
return graph.coords;
|
|
29961
|
+
}
|
|
29962
|
+
return normalizePoints(range, step, defaultLinearCoords[0]);
|
|
29963
|
+
};
|
|
29964
|
+
const getLinearSystemCoords = ({
|
|
29965
|
+
graph,
|
|
29966
|
+
range,
|
|
29967
|
+
step
|
|
29968
|
+
}) => {
|
|
29969
|
+
if (graph.coords) {
|
|
29970
|
+
return graph.coords;
|
|
29880
29971
|
}
|
|
29972
|
+
return defaultLinearCoords.map(points => normalizePoints(range, step, points));
|
|
29881
29973
|
};
|
|
29882
29974
|
const getPolygonCoords = ({
|
|
29883
29975
|
graph,
|
|
@@ -29908,11 +30000,33 @@ const getPolygonCoords = ({
|
|
|
29908
30000
|
coords = normalizePoints(range, step, coords, /* noSnap */!snapToGrid);
|
|
29909
30001
|
return coords;
|
|
29910
30002
|
};
|
|
29911
|
-
const
|
|
29912
|
-
|
|
30003
|
+
const getSinusoidCoords = (graph, range, step) => {
|
|
30004
|
+
if (graph.coords) {
|
|
30005
|
+
return [graph.coords[0], graph.coords[1]];
|
|
30006
|
+
}
|
|
30007
|
+
let coords = [[0.5, 0.5], [0.65, 0.6]];
|
|
29913
30008
|
coords = normalizePoints(range, step, coords, true);
|
|
29914
30009
|
return coords;
|
|
29915
30010
|
};
|
|
30011
|
+
const getQuadraticCoords = (graph, range, step) => {
|
|
30012
|
+
if (graph.coords) {
|
|
30013
|
+
return graph.coords;
|
|
30014
|
+
}
|
|
30015
|
+
const defaultCoords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
|
|
30016
|
+
return normalizePoints(range, step, defaultCoords, true);
|
|
30017
|
+
};
|
|
30018
|
+
const getCircleCoords = (graph, range, step) => {
|
|
30019
|
+
if (graph.center != null && graph.radius != null) {
|
|
30020
|
+
return {
|
|
30021
|
+
center: graph.center,
|
|
30022
|
+
radiusPoint: vec.add(graph.center, [graph.radius, 0])
|
|
30023
|
+
};
|
|
30024
|
+
}
|
|
30025
|
+
return {
|
|
30026
|
+
center: [0, 0],
|
|
30027
|
+
radiusPoint: [1, 0]
|
|
30028
|
+
};
|
|
30029
|
+
};
|
|
29916
30030
|
|
|
29917
30031
|
/**
|
|
29918
30032
|
* determine radius of a circle graph
|
|
@@ -29941,6 +30055,7 @@ function CircleGraph(props) {
|
|
|
29941
30055
|
onMove: newCenter => dispatch(moveCenter(newCenter))
|
|
29942
30056
|
}), /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
29943
30057
|
point: radiusPoint,
|
|
30058
|
+
cursor: "ew-resize",
|
|
29944
30059
|
onMove: newRadiusPoint => {
|
|
29945
30060
|
dispatch(moveRadiusPoint(newRadiusPoint));
|
|
29946
30061
|
}
|
|
@@ -30098,6 +30213,62 @@ const getQuadraticCoefficients = coords => {
|
|
|
30098
30213
|
return [a, b, c];
|
|
30099
30214
|
};
|
|
30100
30215
|
|
|
30216
|
+
function SinusoidGraph(props) {
|
|
30217
|
+
const {
|
|
30218
|
+
dispatch,
|
|
30219
|
+
graphState
|
|
30220
|
+
} = props;
|
|
30221
|
+
|
|
30222
|
+
// Destructure the coordinates from the graph state
|
|
30223
|
+
// Note: The order of the coordinates is important:
|
|
30224
|
+
// The coords[0] is the root and the coords[1] is the first peak
|
|
30225
|
+
const {
|
|
30226
|
+
coords
|
|
30227
|
+
} = graphState;
|
|
30228
|
+
|
|
30229
|
+
// Get the coefficients for calculating the quadratic equation
|
|
30230
|
+
const coeffs = getSinusoidCoefficients(coords);
|
|
30231
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Plot$3.OfX, {
|
|
30232
|
+
y: x => computeSine(x, coeffs),
|
|
30233
|
+
color: color.blue
|
|
30234
|
+
}), coords.map((coord, i) => /*#__PURE__*/React.createElement(StyledMovablePoint, {
|
|
30235
|
+
key: "point-" + i,
|
|
30236
|
+
point: coord,
|
|
30237
|
+
onMove: destination => dispatch(movePoint(i, destination))
|
|
30238
|
+
})));
|
|
30239
|
+
}
|
|
30240
|
+
|
|
30241
|
+
// Plot a sinusoid of the form: f(x) = a * sin(b * x - c) + d
|
|
30242
|
+
const computeSine = function computeSine(x,
|
|
30243
|
+
// x-coordinate
|
|
30244
|
+
sinusoidCoefficients) {
|
|
30245
|
+
// Break down the coefficients for the sine function to improve readability
|
|
30246
|
+
const {
|
|
30247
|
+
amplitude: a,
|
|
30248
|
+
angularFrequency: b,
|
|
30249
|
+
phase: c,
|
|
30250
|
+
verticalOffset: d
|
|
30251
|
+
} = sinusoidCoefficients;
|
|
30252
|
+
return a * Math.sin(b * x - c) + d;
|
|
30253
|
+
};
|
|
30254
|
+
const getSinusoidCoefficients = coords => {
|
|
30255
|
+
// It's assumed that p1 is the root and p2 is the first peak
|
|
30256
|
+
const p1 = coords[0];
|
|
30257
|
+
const p2 = coords[1];
|
|
30258
|
+
|
|
30259
|
+
// Resulting coefficients are canonical for this sine curve
|
|
30260
|
+
const amplitude = p2[1] - p1[1];
|
|
30261
|
+
const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
|
|
30262
|
+
const phase = p1[0] * angularFrequency;
|
|
30263
|
+
const verticalOffset = p1[1];
|
|
30264
|
+
return {
|
|
30265
|
+
amplitude,
|
|
30266
|
+
angularFrequency,
|
|
30267
|
+
phase,
|
|
30268
|
+
verticalOffset
|
|
30269
|
+
};
|
|
30270
|
+
};
|
|
30271
|
+
|
|
30101
30272
|
// Universal padding for the axis ticks to ensure that the labels
|
|
30102
30273
|
// have enough space to render without overlapping the tick lines
|
|
30103
30274
|
const tickPadding = 5;
|
|
@@ -31187,15 +31358,9 @@ function interactiveGraphReducer(state, action) {
|
|
|
31187
31358
|
}
|
|
31188
31359
|
}
|
|
31189
31360
|
function doMoveControlPoint(state, action) {
|
|
31190
|
-
const {
|
|
31191
|
-
snapStep,
|
|
31192
|
-
range
|
|
31193
|
-
} = state;
|
|
31194
31361
|
switch (state.type) {
|
|
31195
31362
|
case "segment":
|
|
31196
|
-
case "linear":
|
|
31197
31363
|
case "linear-system":
|
|
31198
|
-
case "ray":
|
|
31199
31364
|
{
|
|
31200
31365
|
const newCoords = updateAtIndex({
|
|
31201
31366
|
array: state.coords,
|
|
@@ -31203,11 +31368,7 @@ function doMoveControlPoint(state, action) {
|
|
|
31203
31368
|
update: tuple => setAtIndex({
|
|
31204
31369
|
array: tuple,
|
|
31205
31370
|
index: action.pointIndex,
|
|
31206
|
-
newValue:
|
|
31207
|
-
snapStep,
|
|
31208
|
-
range,
|
|
31209
|
-
point: action.destination
|
|
31210
|
-
}))
|
|
31371
|
+
newValue: boundAndSnapToGrid(action.destination, state)
|
|
31211
31372
|
})
|
|
31212
31373
|
});
|
|
31213
31374
|
const coordsToCheck = newCoords[action.itemIndex];
|
|
@@ -31219,16 +31380,33 @@ function doMoveControlPoint(state, action) {
|
|
|
31219
31380
|
coords: newCoords
|
|
31220
31381
|
});
|
|
31221
31382
|
}
|
|
31383
|
+
case "linear":
|
|
31384
|
+
case "ray":
|
|
31385
|
+
{
|
|
31386
|
+
const newCoords = setAtIndex({
|
|
31387
|
+
array: state.coords,
|
|
31388
|
+
index: action.pointIndex,
|
|
31389
|
+
newValue: boundAndSnapToGrid(action.destination, state)
|
|
31390
|
+
});
|
|
31391
|
+
return _extends({}, state, {
|
|
31392
|
+
hasBeenInteractedWith: true,
|
|
31393
|
+
coords: newCoords
|
|
31394
|
+
});
|
|
31395
|
+
}
|
|
31222
31396
|
case "circle":
|
|
31223
31397
|
throw new Error("FIXME implement circle reducer");
|
|
31224
31398
|
case "point":
|
|
31225
31399
|
case "polygon":
|
|
31226
31400
|
case "quadratic":
|
|
31401
|
+
case "sinusoid":
|
|
31227
31402
|
throw new Error(`Don't use moveControlPoint for ${state.type} graphs. Use movePoint instead!`);
|
|
31228
31403
|
default:
|
|
31229
31404
|
throw new UnreachableCaseError(state);
|
|
31230
31405
|
}
|
|
31231
31406
|
}
|
|
31407
|
+
|
|
31408
|
+
// (LEMS-2050): Update the reducer so that we have a separate action for moving one line
|
|
31409
|
+
// and another action for moving multiple lines
|
|
31232
31410
|
function doMoveLine(state, action) {
|
|
31233
31411
|
const {
|
|
31234
31412
|
snapStep,
|
|
@@ -31236,9 +31414,7 @@ function doMoveLine(state, action) {
|
|
|
31236
31414
|
} = state;
|
|
31237
31415
|
switch (state.type) {
|
|
31238
31416
|
case "segment":
|
|
31239
|
-
case "linear":
|
|
31240
31417
|
case "linear-system":
|
|
31241
|
-
case "ray":
|
|
31242
31418
|
{
|
|
31243
31419
|
if (action.itemIndex === undefined) {
|
|
31244
31420
|
throw new Error("Please provide index of line to move");
|
|
@@ -31263,6 +31439,21 @@ function doMoveLine(state, action) {
|
|
|
31263
31439
|
coords: newCoords
|
|
31264
31440
|
});
|
|
31265
31441
|
}
|
|
31442
|
+
case "linear":
|
|
31443
|
+
case "ray":
|
|
31444
|
+
{
|
|
31445
|
+
const currentLine = state.coords;
|
|
31446
|
+
const change = getChange(currentLine, action.delta, {
|
|
31447
|
+
snapStep,
|
|
31448
|
+
range
|
|
31449
|
+
});
|
|
31450
|
+
const newLine = [snap(snapStep, vec.add(currentLine[0], change)), snap(snapStep, vec.add(currentLine[1], change))];
|
|
31451
|
+
return _extends({}, state, {
|
|
31452
|
+
type: state.type,
|
|
31453
|
+
hasBeenInteractedWith: true,
|
|
31454
|
+
coords: newLine
|
|
31455
|
+
});
|
|
31456
|
+
}
|
|
31266
31457
|
default:
|
|
31267
31458
|
// The MoveLine action doesn't make sense for other graph types;
|
|
31268
31459
|
// ignore it if it somehow happens
|
|
@@ -31295,6 +31486,20 @@ function doMoveAll(state, action) {
|
|
|
31295
31486
|
function doMovePoint(state, action) {
|
|
31296
31487
|
switch (state.type) {
|
|
31297
31488
|
case "polygon":
|
|
31489
|
+
const newCoords = setAtIndex({
|
|
31490
|
+
array: state.coords,
|
|
31491
|
+
index: action.index,
|
|
31492
|
+
newValue: boundAndSnapToGrid(action.destination, state)
|
|
31493
|
+
});
|
|
31494
|
+
|
|
31495
|
+
// Reject the move if it would cause the sides of the polygon to cross
|
|
31496
|
+
if (polygonSidesIntersect(newCoords)) {
|
|
31497
|
+
return state;
|
|
31498
|
+
}
|
|
31499
|
+
return _extends({}, state, {
|
|
31500
|
+
hasBeenInteractedWith: true,
|
|
31501
|
+
coords: newCoords
|
|
31502
|
+
});
|
|
31298
31503
|
case "point":
|
|
31299
31504
|
{
|
|
31300
31505
|
return _extends({}, state, {
|
|
@@ -31302,11 +31507,26 @@ function doMovePoint(state, action) {
|
|
|
31302
31507
|
coords: setAtIndex({
|
|
31303
31508
|
array: state.coords,
|
|
31304
31509
|
index: action.index,
|
|
31305
|
-
newValue:
|
|
31306
|
-
|
|
31307
|
-
|
|
31308
|
-
|
|
31309
|
-
|
|
31510
|
+
newValue: boundAndSnapToGrid(action.destination, state)
|
|
31511
|
+
})
|
|
31512
|
+
});
|
|
31513
|
+
}
|
|
31514
|
+
case "sinusoid":
|
|
31515
|
+
{
|
|
31516
|
+
// First, we need to verify that the new coordinates are not on the same vertical line
|
|
31517
|
+
// If they are, we don't want to move the point
|
|
31518
|
+
const destination = action.destination;
|
|
31519
|
+
const _newCoords = [...state.coords];
|
|
31520
|
+
_newCoords[action.index] = action.destination;
|
|
31521
|
+
if (_newCoords[0][0] === _newCoords[1][0]) {
|
|
31522
|
+
return state;
|
|
31523
|
+
}
|
|
31524
|
+
return _extends({}, state, {
|
|
31525
|
+
hasBeenInteractedWith: true,
|
|
31526
|
+
coords: setAtIndex({
|
|
31527
|
+
array: state.coords,
|
|
31528
|
+
index: action.index,
|
|
31529
|
+
newValue: boundAndSnapToGrid(destination, state)
|
|
31310
31530
|
})
|
|
31311
31531
|
});
|
|
31312
31532
|
}
|
|
@@ -31317,11 +31537,7 @@ function doMovePoint(state, action) {
|
|
|
31317
31537
|
coords: setAtIndex({
|
|
31318
31538
|
array: state.coords,
|
|
31319
31539
|
index: action.index,
|
|
31320
|
-
newValue:
|
|
31321
|
-
snapStep: state.snapStep,
|
|
31322
|
-
range: state.range,
|
|
31323
|
-
point: action.destination
|
|
31324
|
-
}))
|
|
31540
|
+
newValue: boundAndSnapToGrid(action.destination, state)
|
|
31325
31541
|
})
|
|
31326
31542
|
});
|
|
31327
31543
|
}
|
|
@@ -31424,6 +31640,17 @@ const getChange = (coords, delta, constraintOpts) => {
|
|
|
31424
31640
|
const dy = clamp(deltaY, minYMove, maxYMove);
|
|
31425
31641
|
return [dx, dy];
|
|
31426
31642
|
};
|
|
31643
|
+
function boundAndSnapToGrid(point, {
|
|
31644
|
+
snapStep,
|
|
31645
|
+
range
|
|
31646
|
+
}) {
|
|
31647
|
+
return snap(snapStep, bound$1({
|
|
31648
|
+
snapStep,
|
|
31649
|
+
range,
|
|
31650
|
+
point
|
|
31651
|
+
}));
|
|
31652
|
+
}
|
|
31653
|
+
|
|
31427
31654
|
// Returns the closest point to the given `point` that is within the graph
|
|
31428
31655
|
// bounds given in `state`.
|
|
31429
31656
|
function bound$1({
|
|
@@ -31513,8 +31740,12 @@ const renderGraph = props => {
|
|
|
31513
31740
|
graphState: state,
|
|
31514
31741
|
dispatch: dispatch
|
|
31515
31742
|
});
|
|
31516
|
-
case "linear":
|
|
31517
31743
|
case "linear-system":
|
|
31744
|
+
return /*#__PURE__*/React.createElement(LinearSystemGraph, {
|
|
31745
|
+
graphState: state,
|
|
31746
|
+
dispatch: dispatch
|
|
31747
|
+
});
|
|
31748
|
+
case "linear":
|
|
31518
31749
|
return /*#__PURE__*/React.createElement(LinearGraph, {
|
|
31519
31750
|
graphState: state,
|
|
31520
31751
|
dispatch: dispatch
|
|
@@ -31544,6 +31775,11 @@ const renderGraph = props => {
|
|
|
31544
31775
|
graphState: state,
|
|
31545
31776
|
dispatch: dispatch
|
|
31546
31777
|
});
|
|
31778
|
+
case "sinusoid":
|
|
31779
|
+
return /*#__PURE__*/React.createElement(SinusoidGraph, {
|
|
31780
|
+
graphState: state,
|
|
31781
|
+
dispatch: dispatch
|
|
31782
|
+
});
|
|
31547
31783
|
default:
|
|
31548
31784
|
return new UnreachableCaseError(type);
|
|
31549
31785
|
}
|
|
@@ -43758,7 +43994,7 @@ const init = function init(options) {
|
|
|
43758
43994
|
|
|
43759
43995
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
43760
43996
|
const libName = "@khanacademy/perseus";
|
|
43761
|
-
const libVersion = "23.
|
|
43997
|
+
const libVersion = "23.3.0";
|
|
43762
43998
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
43763
43999
|
|
|
43764
44000
|
const apiVersion = {
|
|
@@ -46759,5 +46995,5 @@ const EditorJsonify = {
|
|
|
46759
46995
|
}
|
|
46760
46996
|
};
|
|
46761
46997
|
|
|
46762
|
-
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, 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, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
46998
|
+
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, 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, lockedEllipseFillStyles, lockedFigureColors, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
|
|
46763
46999
|
//# sourceMappingURL=index.js.map
|