Merge branch 'develop' into reducer-project
This commit is contained in:
commit
bc86988453
|
@ -4,6 +4,8 @@ import {
|
|||
result,
|
||||
distributionError,
|
||||
distributionErrorToString,
|
||||
squiggleExpression,
|
||||
resultMap,
|
||||
} from "@quri/squiggle-lang";
|
||||
import { Vega } from "react-vega";
|
||||
import { ErrorAlert } from "./Alert";
|
||||
|
@ -14,6 +16,8 @@ import {
|
|||
DistributionChartSpecOptions,
|
||||
} from "../lib/distributionSpecBuilder";
|
||||
import { NumberShower } from "./NumberShower";
|
||||
import { Plot, parsePlot } from "../lib/plotParser";
|
||||
import { flattenResult } from "../lib/utility";
|
||||
import { hasMassBelowZero } from "../lib/distributionUtils";
|
||||
|
||||
export type DistributionPlottingSettings = {
|
||||
|
@ -23,26 +27,41 @@ export type DistributionPlottingSettings = {
|
|||
} & DistributionChartSpecOptions;
|
||||
|
||||
export type DistributionChartProps = {
|
||||
distribution: Distribution;
|
||||
plot: Plot;
|
||||
width?: number;
|
||||
height: number;
|
||||
} & DistributionPlottingSettings;
|
||||
|
||||
export function defaultPlot(distribution: Distribution): Plot {
|
||||
return { distributions: [{ name: "default", distribution }] };
|
||||
}
|
||||
|
||||
export function makePlot(record: {
|
||||
[key: string]: squiggleExpression;
|
||||
}): Plot | void {
|
||||
const plotResult = parsePlot(record);
|
||||
if (plotResult.tag === "Ok") {
|
||||
return plotResult.value;
|
||||
}
|
||||
}
|
||||
|
||||
export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
|
||||
const {
|
||||
distribution,
|
||||
height,
|
||||
showSummary,
|
||||
width,
|
||||
logX,
|
||||
actions = false,
|
||||
} = props;
|
||||
const shape = distribution.pointSet();
|
||||
const { plot, height, showSummary, width, logX, actions = false } = props;
|
||||
const [sized] = useSize((size) => {
|
||||
if (shape.tag === "Error") {
|
||||
let shapes = flattenResult(
|
||||
plot.distributions.map((x) =>
|
||||
resultMap(x.distribution.pointSet(), (shape) => ({
|
||||
name: x.name,
|
||||
// color: x.color, // not supported yet
|
||||
continuous: shape.continuous,
|
||||
discrete: shape.discrete,
|
||||
}))
|
||||
)
|
||||
);
|
||||
if (shapes.tag === "Error") {
|
||||
return (
|
||||
<ErrorAlert heading="Distribution Error">
|
||||
{distributionErrorToString(shape.value)}
|
||||
{distributionErrorToString(shapes.value)}
|
||||
</ErrorAlert>
|
||||
);
|
||||
}
|
||||
|
@ -56,24 +75,29 @@ export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
|
|||
);
|
||||
widthProp = 20;
|
||||
}
|
||||
const domain = shapes.value.flatMap((shape) =>
|
||||
shape.discrete.concat(shape.continuous)
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ width: widthProp }}>
|
||||
{logX && hasMassBelowZero(shape.value) ? (
|
||||
{logX && shapes.value.some(hasMassBelowZero) ? (
|
||||
<ErrorAlert heading="Log Domain Error">
|
||||
Cannot graph distribution with negative values on logarithmic scale.
|
||||
</ErrorAlert>
|
||||
) : (
|
||||
<Vega
|
||||
spec={spec}
|
||||
data={{ con: shape.value.continuous, dis: shape.value.discrete }}
|
||||
data={{ data: shapes.value, domain }}
|
||||
width={widthProp - 10}
|
||||
height={height}
|
||||
actions={actions}
|
||||
/>
|
||||
)}
|
||||
<div className="flex justify-center">
|
||||
{showSummary && <SummaryTable distribution={distribution} />}
|
||||
{showSummary && plot.distributions.length === 1 && (
|
||||
<SummaryTable distribution={plot.distributions[0].distribution} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -16,6 +16,7 @@ import * as percentilesSpec from "../vega-specs/spec-percentiles.json";
|
|||
import {
|
||||
DistributionChart,
|
||||
DistributionPlottingSettings,
|
||||
defaultPlot,
|
||||
} from "./DistributionChart";
|
||||
import { NumberShower } from "./NumberShower";
|
||||
import { ErrorAlert } from "./Alert";
|
||||
|
@ -179,7 +180,7 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
|
|||
let showChart =
|
||||
mouseItem.tag === "Ok" && mouseItem.value.tag === "distribution" ? (
|
||||
<DistributionChart
|
||||
distribution={mouseItem.value.value}
|
||||
plot={defaultPlot(mouseItem.value.value)}
|
||||
width={400}
|
||||
height={50}
|
||||
{...distributionPlotSettings}
|
||||
|
|
|
@ -37,10 +37,7 @@ import { InputItem } from "./ui/InputItem";
|
|||
import { Text } from "./ui/Text";
|
||||
import { ViewSettings, viewSettingsSchema } from "./ViewSettings";
|
||||
import { HeadedSection } from "./ui/HeadedSection";
|
||||
import {
|
||||
defaultColor,
|
||||
defaultTickFormat,
|
||||
} from "../lib/distributionSpecBuilder";
|
||||
import { defaultTickFormat } from "../lib/distributionSpecBuilder";
|
||||
import { Button } from "./ui/Button";
|
||||
|
||||
type PlaygroundProps = SquiggleChartProps & {
|
||||
|
@ -240,7 +237,6 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
|||
title,
|
||||
minX,
|
||||
maxX,
|
||||
color = defaultColor,
|
||||
tickFormat = defaultTickFormat,
|
||||
distributionChartActions,
|
||||
code: controlledCode,
|
||||
|
@ -268,7 +264,6 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
|||
title,
|
||||
minX,
|
||||
maxX,
|
||||
color,
|
||||
tickFormat,
|
||||
distributionChartActions,
|
||||
showSummary,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import { squiggleExpression, declaration } from "@quri/squiggle-lang";
|
||||
import { NumberShower } from "../NumberShower";
|
||||
import { DistributionChart } from "../DistributionChart";
|
||||
import { DistributionChart, defaultPlot, makePlot } from "../DistributionChart";
|
||||
import { FunctionChart, FunctionChartSettings } from "../FunctionChart";
|
||||
import clsx from "clsx";
|
||||
import { VariableBox } from "./VariableBox";
|
||||
|
@ -102,7 +102,7 @@ export const ExpressionViewer: React.FC<Props> = ({
|
|||
{(settings) => {
|
||||
return (
|
||||
<DistributionChart
|
||||
distribution={expression.value}
|
||||
plot={defaultPlot(expression.value)}
|
||||
{...settings.distributionPlotSettings}
|
||||
height={settings.height}
|
||||
width={width}
|
||||
|
@ -241,9 +241,9 @@ export const ExpressionViewer: React.FC<Props> = ({
|
|||
case "module": {
|
||||
return (
|
||||
<VariableList path={path} heading="Module">
|
||||
{(settings) =>
|
||||
{(_) =>
|
||||
Object.entries(expression.value)
|
||||
.filter(([key, r]) => !key.match(/^(Math|System)\./))
|
||||
.filter(([key, _]) => !key.match(/^(Math|System)\./))
|
||||
.map(([key, r]) => (
|
||||
<ExpressionViewer
|
||||
key={key}
|
||||
|
@ -257,24 +257,61 @@ export const ExpressionViewer: React.FC<Props> = ({
|
|||
);
|
||||
}
|
||||
case "record":
|
||||
return (
|
||||
<VariableList path={path} heading="Record">
|
||||
{(settings) =>
|
||||
Object.entries(expression.value).map(([key, r]) => (
|
||||
<ExpressionViewer
|
||||
key={key}
|
||||
path={[...path, key]}
|
||||
expression={r}
|
||||
width={width !== undefined ? width - 20 : width}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</VariableList>
|
||||
);
|
||||
const plot = makePlot(expression.value);
|
||||
if (plot) {
|
||||
return (
|
||||
<VariableBox
|
||||
path={path}
|
||||
heading={"Plot"}
|
||||
renderSettingsMenu={({ onChange }) => {
|
||||
let disableLogX = plot.distributions.some((x) => {
|
||||
let pointSet = x.distribution.pointSet();
|
||||
return (
|
||||
pointSet.tag === "Ok" && hasMassBelowZero(pointSet.value)
|
||||
);
|
||||
});
|
||||
return (
|
||||
<ItemSettingsMenu
|
||||
path={path}
|
||||
onChange={onChange}
|
||||
disableLogX={disableLogX}
|
||||
withFunctionSettings={false}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
>
|
||||
{(settings) => {
|
||||
return (
|
||||
<DistributionChart
|
||||
plot={plot}
|
||||
{...settings.distributionPlotSettings}
|
||||
height={settings.height}
|
||||
width={width}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</VariableBox>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<VariableList path={path} heading="Record">
|
||||
{(_) =>
|
||||
Object.entries(expression.value).map(([key, r]) => (
|
||||
<ExpressionViewer
|
||||
key={key}
|
||||
path={[...path, key]}
|
||||
expression={r}
|
||||
width={width !== undefined ? width - 20 : width}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</VariableList>
|
||||
);
|
||||
}
|
||||
case "array":
|
||||
return (
|
||||
<VariableList path={path} heading="Array">
|
||||
{(settings) =>
|
||||
{(_) =>
|
||||
expression.value.map((r, i) => (
|
||||
<ExpressionViewer
|
||||
key={i}
|
||||
|
|
|
@ -6,10 +6,7 @@ import { Modal } from "../ui/Modal";
|
|||
import { ViewSettings, viewSettingsSchema } from "../ViewSettings";
|
||||
import { Path, pathAsString } from "./utils";
|
||||
import { ViewerContext } from "./ViewerContext";
|
||||
import {
|
||||
defaultColor,
|
||||
defaultTickFormat,
|
||||
} from "../../lib/distributionSpecBuilder";
|
||||
import { defaultTickFormat } from "../../lib/distributionSpecBuilder";
|
||||
import { PlaygroundContext } from "../SquigglePlayground";
|
||||
|
||||
type Props = {
|
||||
|
@ -46,7 +43,6 @@ const ItemSettingsModal: React.FC<
|
|||
tickFormat:
|
||||
mergedSettings.distributionPlotSettings.format || defaultTickFormat,
|
||||
title: mergedSettings.distributionPlotSettings.title,
|
||||
color: mergedSettings.distributionPlotSettings.color || defaultColor,
|
||||
minX: mergedSettings.distributionPlotSettings.minX,
|
||||
maxX: mergedSettings.distributionPlotSettings.maxX,
|
||||
distributionChartActions: mergedSettings.distributionPlotSettings.actions,
|
||||
|
@ -66,7 +62,6 @@ const ItemSettingsModal: React.FC<
|
|||
expY: vars.expY,
|
||||
format: vars.tickFormat,
|
||||
title: vars.title,
|
||||
color: vars.color,
|
||||
minX: vars.minX,
|
||||
maxX: vars.maxX,
|
||||
actions: vars.distributionChartActions,
|
||||
|
|
|
@ -5,10 +5,7 @@ import { InputItem } from "./ui/InputItem";
|
|||
import { Checkbox } from "./ui/Checkbox";
|
||||
import { HeadedSection } from "./ui/HeadedSection";
|
||||
import { Text } from "./ui/Text";
|
||||
import {
|
||||
defaultColor,
|
||||
defaultTickFormat,
|
||||
} from "../lib/distributionSpecBuilder";
|
||||
import { defaultTickFormat } from "../lib/distributionSpecBuilder";
|
||||
|
||||
export const viewSettingsSchema = yup.object({}).shape({
|
||||
chartHeight: yup.number().required().positive().integer().default(350),
|
||||
|
@ -18,7 +15,6 @@ export const viewSettingsSchema = yup.object({}).shape({
|
|||
expY: yup.boolean().required(),
|
||||
tickFormat: yup.string().default(defaultTickFormat),
|
||||
title: yup.string(),
|
||||
color: yup.string().default(defaultColor).required(),
|
||||
minX: yup.number(),
|
||||
maxX: yup.number(),
|
||||
distributionChartActions: yup.boolean(),
|
||||
|
@ -114,12 +110,6 @@ export const ViewSettings: React.FC<{
|
|||
register={register}
|
||||
label="Tick Format"
|
||||
/>
|
||||
<InputItem
|
||||
name="color"
|
||||
type="color"
|
||||
register={register}
|
||||
label="Color"
|
||||
/>
|
||||
</div>
|
||||
</HeadedSection>
|
||||
</div>
|
||||
|
|
|
@ -10,8 +10,6 @@ export type DistributionChartSpecOptions = {
|
|||
minX?: number;
|
||||
/** The maximum x coordinate shown on the chart */
|
||||
maxX?: number;
|
||||
/** The color of the chart */
|
||||
color?: string;
|
||||
/** The title of the chart */
|
||||
title?: string;
|
||||
/** The formatting of the ticks */
|
||||
|
@ -25,36 +23,14 @@ export let linearXScale: LinearScale = {
|
|||
range: "width",
|
||||
zero: false,
|
||||
nice: false,
|
||||
domain: {
|
||||
fields: [
|
||||
{
|
||||
data: "con",
|
||||
field: "x",
|
||||
},
|
||||
{
|
||||
data: "dis",
|
||||
field: "x",
|
||||
},
|
||||
],
|
||||
},
|
||||
domain: { data: "domain", field: "x" },
|
||||
};
|
||||
export let linearYScale: LinearScale = {
|
||||
name: "yscale",
|
||||
type: "linear",
|
||||
range: "height",
|
||||
zero: true,
|
||||
domain: {
|
||||
fields: [
|
||||
{
|
||||
data: "con",
|
||||
field: "y",
|
||||
},
|
||||
{
|
||||
data: "dis",
|
||||
field: "y",
|
||||
},
|
||||
],
|
||||
},
|
||||
domain: { data: "domain", field: "y" },
|
||||
};
|
||||
|
||||
export let logXScale: LogScale = {
|
||||
|
@ -65,18 +41,7 @@ export let logXScale: LogScale = {
|
|||
base: 10,
|
||||
nice: false,
|
||||
clamp: true,
|
||||
domain: {
|
||||
fields: [
|
||||
{
|
||||
data: "con",
|
||||
field: "x",
|
||||
},
|
||||
{
|
||||
data: "dis",
|
||||
field: "x",
|
||||
},
|
||||
],
|
||||
},
|
||||
domain: { data: "domain", field: "x" },
|
||||
};
|
||||
|
||||
export let expYScale: PowScale = {
|
||||
|
@ -86,29 +51,16 @@ export let expYScale: PowScale = {
|
|||
range: "height",
|
||||
zero: true,
|
||||
nice: false,
|
||||
domain: {
|
||||
fields: [
|
||||
{
|
||||
data: "con",
|
||||
field: "y",
|
||||
},
|
||||
{
|
||||
data: "dis",
|
||||
field: "y",
|
||||
},
|
||||
],
|
||||
},
|
||||
domain: { data: "domain", field: "y" },
|
||||
};
|
||||
|
||||
export const defaultTickFormat = ".9~s";
|
||||
export const defaultColor = "#739ECC";
|
||||
|
||||
export function buildVegaSpec(
|
||||
specOptions: DistributionChartSpecOptions
|
||||
): VisualizationSpec {
|
||||
let {
|
||||
const {
|
||||
format = defaultTickFormat,
|
||||
color = defaultColor,
|
||||
title,
|
||||
minX,
|
||||
maxX,
|
||||
|
@ -127,20 +79,32 @@ export function buildVegaSpec(
|
|||
|
||||
let spec: VisualizationSpec = {
|
||||
$schema: "https://vega.github.io/schema/vega/v5.json",
|
||||
description: "A basic area chart example",
|
||||
description: "Squiggle plot chart",
|
||||
width: 500,
|
||||
height: 100,
|
||||
padding: 5,
|
||||
data: [
|
||||
{
|
||||
name: "con",
|
||||
name: "data",
|
||||
},
|
||||
{
|
||||
name: "dis",
|
||||
name: "domain",
|
||||
},
|
||||
],
|
||||
signals: [],
|
||||
scales: [xScale, expY ? expYScale : linearYScale],
|
||||
scales: [
|
||||
xScale,
|
||||
expY ? expYScale : linearYScale,
|
||||
{
|
||||
name: "color",
|
||||
type: "ordinal",
|
||||
domain: {
|
||||
data: "data",
|
||||
field: "name",
|
||||
},
|
||||
range: { scheme: "blues" },
|
||||
},
|
||||
],
|
||||
axes: [
|
||||
{
|
||||
orient: "bottom",
|
||||
|
@ -152,108 +116,178 @@ export function buildVegaSpec(
|
|||
domainOpacity: 0.0,
|
||||
format: format,
|
||||
tickCount: 10,
|
||||
labelOverlap: "greedy",
|
||||
},
|
||||
],
|
||||
marks: [
|
||||
{
|
||||
type: "area",
|
||||
name: "all_distributions",
|
||||
type: "group",
|
||||
from: {
|
||||
data: "con",
|
||||
},
|
||||
encode: {
|
||||
update: {
|
||||
interpolate: { value: "linear" },
|
||||
x: {
|
||||
scale: "xscale",
|
||||
field: "x",
|
||||
},
|
||||
y: {
|
||||
scale: "yscale",
|
||||
field: "y",
|
||||
},
|
||||
y2: {
|
||||
scale: "yscale",
|
||||
value: 0,
|
||||
},
|
||||
fill: {
|
||||
value: color,
|
||||
},
|
||||
fillOpacity: {
|
||||
value: 1,
|
||||
},
|
||||
facet: {
|
||||
name: "distribution_facet",
|
||||
data: "data",
|
||||
groupby: ["name"],
|
||||
},
|
||||
},
|
||||
marks: [
|
||||
{
|
||||
name: "continuous_distribution",
|
||||
type: "group",
|
||||
from: {
|
||||
facet: {
|
||||
name: "continuous_facet",
|
||||
data: "distribution_facet",
|
||||
field: "continuous",
|
||||
},
|
||||
},
|
||||
encode: {
|
||||
update: {},
|
||||
},
|
||||
marks: [
|
||||
{
|
||||
name: "continuous_area",
|
||||
type: "area",
|
||||
from: {
|
||||
data: "continuous_facet",
|
||||
},
|
||||
encode: {
|
||||
update: {
|
||||
interpolate: { value: "linear" },
|
||||
x: {
|
||||
scale: "xscale",
|
||||
field: "x",
|
||||
},
|
||||
y: {
|
||||
scale: "yscale",
|
||||
field: "y",
|
||||
},
|
||||
fill: {
|
||||
scale: "color",
|
||||
field: { parent: "name" },
|
||||
},
|
||||
y2: {
|
||||
scale: "yscale",
|
||||
value: 0,
|
||||
},
|
||||
fillOpacity: {
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "discrete_distribution",
|
||||
type: "group",
|
||||
from: {
|
||||
facet: {
|
||||
name: "discrete_facet",
|
||||
data: "distribution_facet",
|
||||
field: "discrete",
|
||||
},
|
||||
},
|
||||
marks: [
|
||||
{
|
||||
type: "rect",
|
||||
from: {
|
||||
data: "discrete_facet",
|
||||
},
|
||||
encode: {
|
||||
enter: {
|
||||
width: {
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
update: {
|
||||
x: {
|
||||
scale: "xscale",
|
||||
field: "x",
|
||||
},
|
||||
y: {
|
||||
scale: "yscale",
|
||||
field: "y",
|
||||
},
|
||||
y2: {
|
||||
scale: "yscale",
|
||||
value: 0,
|
||||
},
|
||||
fill: {
|
||||
scale: "color",
|
||||
field: { parent: "name" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "symbol",
|
||||
from: {
|
||||
data: "discrete_facet",
|
||||
},
|
||||
encode: {
|
||||
enter: {
|
||||
shape: {
|
||||
value: "circle",
|
||||
},
|
||||
size: [{ value: 100 }],
|
||||
tooltip: {
|
||||
signal: "{ probability: datum.y, value: datum.x }",
|
||||
},
|
||||
},
|
||||
update: {
|
||||
x: {
|
||||
scale: "xscale",
|
||||
field: "x",
|
||||
},
|
||||
y: {
|
||||
scale: "yscale",
|
||||
field: "y",
|
||||
},
|
||||
fill: {
|
||||
scale: "color",
|
||||
field: { parent: "name" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
legends: [
|
||||
{
|
||||
type: "rect",
|
||||
from: {
|
||||
data: "dis",
|
||||
},
|
||||
fill: "color",
|
||||
orient: "top",
|
||||
labelFontSize: 12,
|
||||
encode: {
|
||||
enter: {
|
||||
width: {
|
||||
value: 1,
|
||||
symbols: {
|
||||
update: {
|
||||
fill: [
|
||||
{ test: "length(domain('color')) == 1", value: "transparent" },
|
||||
{ scale: "color", field: "value" },
|
||||
],
|
||||
},
|
||||
},
|
||||
update: {
|
||||
x: {
|
||||
scale: "xscale",
|
||||
field: "x",
|
||||
},
|
||||
y: {
|
||||
scale: "yscale",
|
||||
field: "y",
|
||||
},
|
||||
y2: {
|
||||
scale: "yscale",
|
||||
value: 0,
|
||||
},
|
||||
fill: {
|
||||
value: "#2f65a7",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "symbol",
|
||||
from: {
|
||||
data: "dis",
|
||||
},
|
||||
encode: {
|
||||
enter: {
|
||||
shape: {
|
||||
value: "circle",
|
||||
},
|
||||
size: [{ value: 100 }],
|
||||
tooltip: {
|
||||
signal: "{ probability: datum.y, value: datum.x }",
|
||||
},
|
||||
},
|
||||
update: {
|
||||
x: {
|
||||
scale: "xscale",
|
||||
field: "x",
|
||||
},
|
||||
y: {
|
||||
scale: "yscale",
|
||||
field: "y",
|
||||
},
|
||||
fill: {
|
||||
value: "#1e4577",
|
||||
labels: {
|
||||
interactive: true,
|
||||
update: {
|
||||
fill: [
|
||||
{ test: "length(domain('color')) == 1", value: "transparent" },
|
||||
{ value: "black" },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
if (title) {
|
||||
spec = {
|
||||
...spec,
|
||||
...(title && {
|
||||
title: {
|
||||
text: title,
|
||||
},
|
||||
};
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
return spec;
|
||||
}
|
||||
|
|
72
packages/components/src/lib/plotParser.ts
Normal file
72
packages/components/src/lib/plotParser.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
import * as yup from "yup";
|
||||
import { Distribution, result, squiggleExpression } from "@quri/squiggle-lang";
|
||||
|
||||
export type LabeledDistribution = {
|
||||
name: string;
|
||||
distribution: Distribution;
|
||||
color?: string;
|
||||
};
|
||||
|
||||
export type Plot = {
|
||||
distributions: LabeledDistribution[];
|
||||
};
|
||||
|
||||
function error<a, b>(err: b): result<a, b> {
|
||||
return { tag: "Error", value: err };
|
||||
}
|
||||
|
||||
function ok<a, b>(x: a): result<a, b> {
|
||||
return { tag: "Ok", value: x };
|
||||
}
|
||||
|
||||
const schema = yup
|
||||
.object()
|
||||
.strict()
|
||||
.noUnknown()
|
||||
.shape({
|
||||
distributions: yup.object().shape({
|
||||
tag: yup.mixed().oneOf(["array"]),
|
||||
value: yup
|
||||
.array()
|
||||
.of(
|
||||
yup.object().shape({
|
||||
tag: yup.mixed().oneOf(["record"]),
|
||||
value: yup.object({
|
||||
name: yup.object().shape({
|
||||
tag: yup.mixed().oneOf(["string"]),
|
||||
value: yup.string().required(),
|
||||
}),
|
||||
// color: yup
|
||||
// .object({
|
||||
// tag: yup.mixed().oneOf(["string"]),
|
||||
// value: yup.string().required(),
|
||||
// })
|
||||
// .default(undefined),
|
||||
distribution: yup.object({
|
||||
tag: yup.mixed().oneOf(["distribution"]),
|
||||
value: yup.mixed(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
)
|
||||
.required(),
|
||||
}),
|
||||
});
|
||||
|
||||
export function parsePlot(record: {
|
||||
[key: string]: squiggleExpression;
|
||||
}): result<Plot, string> {
|
||||
try {
|
||||
const plotRecord = schema.validateSync(record);
|
||||
return ok({
|
||||
distributions: plotRecord.distributions.value.map((x) => ({
|
||||
name: x.value.name.value,
|
||||
// color: x.value.color?.value, // not supported yet
|
||||
distribution: x.value.distribution.value,
|
||||
})),
|
||||
});
|
||||
} catch (e) {
|
||||
const message = e instanceof Error ? e.message : "Unknown error";
|
||||
return error(message);
|
||||
}
|
||||
}
|
37
packages/components/src/lib/utility.ts
Normal file
37
packages/components/src/lib/utility.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { result } from "@quri/squiggle-lang";
|
||||
|
||||
export function flattenResult<a, b>(x: result<a, b>[]): result<a[], b> {
|
||||
if (x.length === 0) {
|
||||
return { tag: "Ok", value: [] };
|
||||
} else {
|
||||
if (x[0].tag === "Error") {
|
||||
return x[0];
|
||||
} else {
|
||||
let rest = flattenResult(x.splice(1));
|
||||
if (rest.tag === "Error") {
|
||||
return rest;
|
||||
} else {
|
||||
return { tag: "Ok", value: [x[0].value].concat(rest.value) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function resultBind<a, b, c>(
|
||||
x: result<a, b>,
|
||||
fn: (y: a) => result<c, b>
|
||||
): result<c, b> {
|
||||
if (x.tag === "Ok") {
|
||||
return fn(x.value);
|
||||
} else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
export function all(arr: boolean[]): boolean {
|
||||
return arr.reduce((x, y) => x && y, true);
|
||||
}
|
||||
|
||||
export function some(arr: boolean[]): boolean {
|
||||
return arr.reduce((x, y) => x || y, false);
|
||||
}
|
|
@ -93,6 +93,33 @@ could be continuous, discrete or mixed.
|
|||
</Story>
|
||||
</Canvas>
|
||||
|
||||
## Multiple plots
|
||||
|
||||
<Canvas>
|
||||
<Story
|
||||
name="Multiple plots"
|
||||
args={{
|
||||
code: `
|
||||
{
|
||||
distributions: [
|
||||
{
|
||||
name: "one",
|
||||
distribution: mx(0.5, normal(0,1))
|
||||
},
|
||||
{
|
||||
name: "two",
|
||||
distribution: mx(2, normal(5, 2)),
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
width,
|
||||
}}
|
||||
>
|
||||
{Template.bind({})}
|
||||
</Story>
|
||||
</Canvas>
|
||||
|
||||
## Constants
|
||||
|
||||
A constant is a simple number as a result. This has special formatting rules
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
"@docusaurus/core": "2.0.1",
|
||||
"@docusaurus/preset-classic": "2.0.1",
|
||||
"@heroicons/react": "^1.0.6",
|
||||
"@quri/squiggle-components": "^0.2.23",
|
||||
"@quri/squiggle-components": "^0.3",
|
||||
"base64-js": "^1.5.1",
|
||||
"clsx": "^1.2.1",
|
||||
"hast-util-is-element": "2.1.2",
|
||||
|
|
122
yarn.lock
122
yarn.lock
|
@ -2188,23 +2188,11 @@
|
|||
minimatch "^3.1.2"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@floating-ui/core@^0.7.3":
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-0.7.3.tgz#d274116678ffae87f6b60e90f88cc4083eefab86"
|
||||
integrity sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==
|
||||
|
||||
"@floating-ui/core@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.0.0.tgz#ec1d31f54c72dd0460276e2149e59bd13c0f01f6"
|
||||
integrity sha512-sm3nW0hHAxTv3gRDdCH8rNVQxijF+qPFo5gAeXCErRjKC7Qc28lIQ3R9Vd7Gw+KgwfA7RhRydDFuGeI0peGq7A==
|
||||
|
||||
"@floating-ui/dom@^0.5.3":
|
||||
version "0.5.4"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-0.5.4.tgz#4eae73f78bcd4bd553ae2ade30e6f1f9c73fe3f1"
|
||||
integrity sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==
|
||||
dependencies:
|
||||
"@floating-ui/core" "^0.7.3"
|
||||
|
||||
"@floating-ui/dom@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.0.0.tgz#66923a56755b6cb7a5958ecf25fe293912672d65"
|
||||
|
@ -2212,15 +2200,6 @@
|
|||
dependencies:
|
||||
"@floating-ui/core" "^1.0.0"
|
||||
|
||||
"@floating-ui/react-dom-interactions@^0.6.6":
|
||||
version "0.6.6"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom-interactions/-/react-dom-interactions-0.6.6.tgz#8542e8c4bcbee2cd0d512de676c6a493e0a2d168"
|
||||
integrity sha512-qnao6UPjSZNHnXrF+u4/n92qVroQkx0Umlhy3Avk1oIebm/5ee6yvDm4xbHob0OjY7ya8WmUnV3rQlPwX3Atwg==
|
||||
dependencies:
|
||||
"@floating-ui/react-dom" "^0.7.2"
|
||||
aria-hidden "^1.1.3"
|
||||
use-isomorphic-layout-effect "^1.1.1"
|
||||
|
||||
"@floating-ui/react-dom-interactions@^0.9.2":
|
||||
version "0.9.2"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom-interactions/-/react-dom-interactions-0.9.2.tgz#9a364cc44ecbc242b5218dff0e0d071de115e13a"
|
||||
|
@ -2229,14 +2208,6 @@
|
|||
"@floating-ui/react-dom" "^1.0.0"
|
||||
aria-hidden "^1.1.3"
|
||||
|
||||
"@floating-ui/react-dom@^0.7.2":
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-0.7.2.tgz#0bf4ceccb777a140fc535c87eb5d6241c8e89864"
|
||||
integrity sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==
|
||||
dependencies:
|
||||
"@floating-ui/dom" "^0.5.3"
|
||||
use-isomorphic-layout-effect "^1.1.1"
|
||||
|
||||
"@floating-ui/react-dom@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-1.0.0.tgz#e0975966694433f1f0abffeee5d8e6bb69b7d16e"
|
||||
|
@ -2279,7 +2250,7 @@
|
|||
resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-1.0.6.tgz#35dd26987228b39ef2316db3b1245c42eb19e324"
|
||||
integrity sha512-JJCXydOFWMDpCP4q13iEplA503MQO3xLoZiKum+955ZCtHINWnx26CUxVxxFQu/uLb4LW3ge15ZpzIkXKkJ8oQ==
|
||||
|
||||
"@hookform/resolvers@^2.9.6", "@hookform/resolvers@^2.9.7":
|
||||
"@hookform/resolvers@^2.9.7":
|
||||
version "2.9.7"
|
||||
resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-2.9.7.tgz#8b257ae67234ce0270e6b044c1a61fb98ec02b4b"
|
||||
integrity sha512-BloehX3MOLwuFEwT4yZnmolPjVmqyn8VsSuodLfazbCIqxBHsQ4qUZsi+bvNNCduRli1AGWFrkDLGD5QoNzsoA==
|
||||
|
@ -2683,7 +2654,7 @@
|
|||
resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b"
|
||||
integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==
|
||||
|
||||
"@motionone/animation@^10.12.0", "@motionone/animation@^10.13.1":
|
||||
"@motionone/animation@^10.13.1":
|
||||
version "10.13.2"
|
||||
resolved "https://registry.yarnpkg.com/@motionone/animation/-/animation-10.13.2.tgz#174a55a3bac1b6fb314cc1c3627093dc790ae081"
|
||||
integrity sha512-YGWss58IR2X4lOjW89rv1Q+/Nq/QhfltaggI7i8sZTpKC1yUvM+XYDdvlRpWc6dk8LviMBrddBJAlLdbaqeRmw==
|
||||
|
@ -2693,18 +2664,6 @@
|
|||
"@motionone/utils" "^10.13.2"
|
||||
tslib "^2.3.1"
|
||||
|
||||
"@motionone/dom@10.12.0":
|
||||
version "10.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@motionone/dom/-/dom-10.12.0.tgz#ae30827fd53219efca4e1150a5ff2165c28351ed"
|
||||
integrity sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==
|
||||
dependencies:
|
||||
"@motionone/animation" "^10.12.0"
|
||||
"@motionone/generators" "^10.12.0"
|
||||
"@motionone/types" "^10.12.0"
|
||||
"@motionone/utils" "^10.12.0"
|
||||
hey-listen "^1.0.8"
|
||||
tslib "^2.3.1"
|
||||
|
||||
"@motionone/dom@10.13.1":
|
||||
version "10.13.1"
|
||||
resolved "https://registry.yarnpkg.com/@motionone/dom/-/dom-10.13.1.tgz#fc29ea5d12538f21b211b3168e502cfc07a24882"
|
||||
|
@ -2725,7 +2684,7 @@
|
|||
"@motionone/utils" "^10.13.2"
|
||||
tslib "^2.3.1"
|
||||
|
||||
"@motionone/generators@^10.12.0", "@motionone/generators@^10.13.1":
|
||||
"@motionone/generators@^10.13.1":
|
||||
version "10.13.2"
|
||||
resolved "https://registry.yarnpkg.com/@motionone/generators/-/generators-10.13.2.tgz#dd972195b899e7a556d65bd27fae2fd423055e10"
|
||||
integrity sha512-QMoXV1MXEEhR6D3dct/RMMS1FwJlAsW+kMPbFGzBA4NbweblgeYQCft9DcDAVpV9wIwD6qvlBG9u99sOXLfHiA==
|
||||
|
@ -2734,12 +2693,12 @@
|
|||
"@motionone/utils" "^10.13.2"
|
||||
tslib "^2.3.1"
|
||||
|
||||
"@motionone/types@^10.12.0", "@motionone/types@^10.13.0", "@motionone/types@^10.13.2":
|
||||
"@motionone/types@^10.13.0", "@motionone/types@^10.13.2":
|
||||
version "10.13.2"
|
||||
resolved "https://registry.yarnpkg.com/@motionone/types/-/types-10.13.2.tgz#c560090d81bd0149e7451aae23ab7af458570363"
|
||||
integrity sha512-yYV4q5v5F0iADhab4wHfqaRJnM/eVtQLjUPhyEcS72aUz/xyOzi09GzD/Gu+K506BDfqn5eULIilUI77QNaqhw==
|
||||
|
||||
"@motionone/utils@^10.12.0", "@motionone/utils@^10.13.1", "@motionone/utils@^10.13.2":
|
||||
"@motionone/utils@^10.13.1", "@motionone/utils@^10.13.2":
|
||||
version "10.13.2"
|
||||
resolved "https://registry.yarnpkg.com/@motionone/utils/-/utils-10.13.2.tgz#ce79bfe1d133493c217cdc0584960434e065648d"
|
||||
integrity sha512-6Lw5bDA/w7lrPmT/jYWQ76lkHlHs9fl2NZpJ22cVy1kKDdEH+Cl1U6hMTpdphO6VQktQ6v2APngag91WBKLqlA==
|
||||
|
@ -2818,33 +2777,7 @@
|
|||
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1"
|
||||
integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==
|
||||
|
||||
"@quri/squiggle-components@^0.2.23":
|
||||
version "0.2.24"
|
||||
resolved "https://registry.yarnpkg.com/@quri/squiggle-components/-/squiggle-components-0.2.24.tgz#16a2d72fb16f46a0bf71388c85d1238927676923"
|
||||
integrity sha512-slBGryELfCsM6WX+AwQcqiPPoImLRHNyXZDueL7a+OKEAx09w3pKOqVzLWNGL7+dJe3dF8as9X/Gv1JbbIj5yw==
|
||||
dependencies:
|
||||
"@floating-ui/react-dom" "^0.7.2"
|
||||
"@floating-ui/react-dom-interactions" "^0.6.6"
|
||||
"@headlessui/react" "^1.6.6"
|
||||
"@heroicons/react" "^1.0.6"
|
||||
"@hookform/resolvers" "^2.9.6"
|
||||
"@quri/squiggle-lang" "^0.2.8"
|
||||
"@react-hook/size" "^2.1.2"
|
||||
clsx "^1.2.1"
|
||||
framer-motion "^6.5.1"
|
||||
lodash "^4.17.21"
|
||||
react "^18.1.0"
|
||||
react-ace "^10.1.0"
|
||||
react-hook-form "^7.33.1"
|
||||
react-use "^17.4.0"
|
||||
react-vega "^7.6.0"
|
||||
vega "^5.22.1"
|
||||
vega-embed "^6.21.0"
|
||||
vega-lite "^5.3.0"
|
||||
vscode-uri "^3.0.3"
|
||||
yup "^0.32.11"
|
||||
|
||||
"@quri/squiggle-lang@^0.2.11", "@quri/squiggle-lang@^0.2.8":
|
||||
"@quri/squiggle-lang@^0.2.11":
|
||||
version "0.2.12"
|
||||
resolved "https://registry.yarnpkg.com/@quri/squiggle-lang/-/squiggle-lang-0.2.12.tgz#e8fdb22a84aa75df71c071d1ed4ae5c55f15d447"
|
||||
integrity sha512-fgv9DLvPlX/TqPSacKSW3GZ5S9H/YwqaMoRdFrn5SJjHnnMh/xJW/9iyzzgOxPCXov9xFeDvL159tkbStMm7vw==
|
||||
|
@ -9721,20 +9654,6 @@ fragment-cache@^0.2.1:
|
|||
dependencies:
|
||||
map-cache "^0.2.2"
|
||||
|
||||
framer-motion@^6.5.1:
|
||||
version "6.5.1"
|
||||
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-6.5.1.tgz#802448a16a6eb764124bf36d8cbdfa6dd6b931a7"
|
||||
integrity sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==
|
||||
dependencies:
|
||||
"@motionone/dom" "10.12.0"
|
||||
framesync "6.0.1"
|
||||
hey-listen "^1.0.8"
|
||||
popmotion "11.0.3"
|
||||
style-value-types "5.0.0"
|
||||
tslib "^2.1.0"
|
||||
optionalDependencies:
|
||||
"@emotion/is-prop-valid" "^0.8.2"
|
||||
|
||||
framer-motion@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-7.1.1.tgz#4d56ed18a7cf2c6a1a4a1af5b57714f8e6b52d9e"
|
||||
|
@ -9749,13 +9668,6 @@ framer-motion@^7.1.1:
|
|||
optionalDependencies:
|
||||
"@emotion/is-prop-valid" "^0.8.2"
|
||||
|
||||
framesync@6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/framesync/-/framesync-6.0.1.tgz#5e32fc01f1c42b39c654c35b16440e07a25d6f20"
|
||||
integrity sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==
|
||||
dependencies:
|
||||
tslib "^2.1.0"
|
||||
|
||||
framesync@6.1.2:
|
||||
version "6.1.2"
|
||||
resolved "https://registry.yarnpkg.com/framesync/-/framesync-6.1.2.tgz#755eff2fb5b8f3b4d2b266dd18121b300aefea27"
|
||||
|
@ -13963,16 +13875,6 @@ polished@^4.2.2:
|
|||
dependencies:
|
||||
"@babel/runtime" "^7.17.8"
|
||||
|
||||
popmotion@11.0.3:
|
||||
version "11.0.3"
|
||||
resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.3.tgz#565c5f6590bbcddab7a33a074bb2ba97e24b0cc9"
|
||||
integrity sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==
|
||||
dependencies:
|
||||
framesync "6.0.1"
|
||||
hey-listen "^1.0.8"
|
||||
style-value-types "5.0.0"
|
||||
tslib "^2.1.0"
|
||||
|
||||
popmotion@11.0.5:
|
||||
version "11.0.5"
|
||||
resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.5.tgz#8e3e014421a0ffa30ecd722564fd2558954e1f7d"
|
||||
|
@ -15165,7 +15067,7 @@ react-helmet-async@*, react-helmet-async@^1.3.0:
|
|||
react-fast-compare "^3.2.0"
|
||||
shallowequal "^1.1.0"
|
||||
|
||||
react-hook-form@^7.33.1, react-hook-form@^7.34.1:
|
||||
react-hook-form@^7.34.1:
|
||||
version "7.34.1"
|
||||
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.34.1.tgz#06cb216daf706bf9ae4969747115afae0d09410d"
|
||||
integrity sha512-tH7TaZgAURMhjzVE2M/EFmxHz2HdaPMAVs9FXTweNW551VlhXSuVcpcYlkiMZf2zHQiTztupVFpBHJFTma+N7w==
|
||||
|
@ -16972,14 +16874,6 @@ style-to-object@0.3.0, style-to-object@^0.3.0:
|
|||
dependencies:
|
||||
inline-style-parser "0.1.1"
|
||||
|
||||
style-value-types@5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-5.0.0.tgz#76c35f0e579843d523187989da866729411fc8ad"
|
||||
integrity sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==
|
||||
dependencies:
|
||||
hey-listen "^1.0.8"
|
||||
tslib "^2.1.0"
|
||||
|
||||
style-value-types@5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-5.1.2.tgz#6be66b237bd546048a764883528072ed95713b62"
|
||||
|
@ -18236,7 +18130,7 @@ vega-label@~1.2.0:
|
|||
vega-scenegraph "^4.9.2"
|
||||
vega-util "^1.15.2"
|
||||
|
||||
vega-lite@^5.3.0, vega-lite@^5.4.0:
|
||||
vega-lite@^5.4.0:
|
||||
version "5.4.0"
|
||||
resolved "https://registry.yarnpkg.com/vega-lite/-/vega-lite-5.4.0.tgz#d09331e2a1c87843d5865de0fa7704919796ab56"
|
||||
integrity sha512-e/P5iOtBE62WEWZhKP7sLcBd92YS9prfUQafelxoOeloooSSrkUwM/ZDmN5Q5ffByEZTiKfODtnwD6/xKDYUmw==
|
||||
|
|
Loading…
Reference in New Issue
Block a user