Added ability to add-remove dists

This commit is contained in:
Ozzie Gooen 2020-03-03 21:30:33 +00:00
parent bdeb53a6c4
commit f45b4b763a
3 changed files with 190 additions and 72 deletions

View File

@ -1,15 +1,4 @@
type state = { open DistPlusPlotReducer;
log: bool,
showStats: bool,
showParams: bool,
height: int,
};
type action =
| CHANGE_LOG
| CHANGE_SHOW_STATS
| CHANGE_SHOW_PARAMS
| CHANGE_HEIGHT(int);
let showAsForm = (distPlus: DistTypes.distPlus) => { let showAsForm = (distPlus: DistTypes.distPlus) => {
<div> <div>
@ -146,7 +135,7 @@ let adjustBoth = discreteProbabilityMass => {
module DistPlusChart = { module DistPlusChart = {
[@react.component] [@react.component]
let make = (~distPlus: DistTypes.distPlus, ~state: state, ~onHover) => { let make = (~distPlus: DistTypes.distPlus, ~config: chartConfig, ~onHover) => {
open Distributions.DistPlus; open Distributions.DistPlus;
let discrete = distPlus |> T.toScaledDiscrete; let discrete = distPlus |> T.toScaledDiscrete;
let continuous = let continuous =
@ -166,27 +155,25 @@ module DistPlusChart = {
distPlus |> Distributions.DistPlus.T.toDiscreteProbabilityMass; distPlus |> Distributions.DistPlus.T.toDiscreteProbabilityMass;
let (yMaxDiscreteDomainFactor, yMaxContinuousDomainFactor) = let (yMaxDiscreteDomainFactor, yMaxContinuousDomainFactor) =
adjustBoth(toDiscreteProbabilityMass); adjustBoth(toDiscreteProbabilityMass);
<div className=Css.(style([minHeight(`px(state.height))]))>
<DistributionPlot <DistributionPlot
scale={state.log ? "log" : "linear"} scale={config.log ? "log" : "linear"}
height={DistPlusPlotReducer.heightToPix(config.height)}
minX minX
maxX maxX
yMaxDiscreteDomainFactor yMaxDiscreteDomainFactor
yMaxContinuousDomainFactor yMaxContinuousDomainFactor
height={state.height}
?discrete ?discrete
?continuous ?continuous
color={`hex("5f6b7e")} color={`hex("5f6b7e")}
onHover onHover
timeScale timeScale
/> />;
</div>;
}; };
}; };
module IntegralChart = { module IntegralChart = {
[@react.component] [@react.component]
let make = (~distPlus: DistTypes.distPlus, ~onHover) => { let make = (~distPlus: DistTypes.distPlus, ~config: chartConfig, ~onHover) => {
open Distributions.DistPlus; open Distributions.DistPlus;
let integral = let integral =
Distributions.DistPlus.T.toShape(distPlus) Distributions.DistPlus.T.toShape(distPlus)
@ -204,9 +191,10 @@ module IntegralChart = {
let maxX = integral |> Distributions.Continuous.T.maxX; let maxX = integral |> Distributions.Continuous.T.maxX;
let timeScale = distPlus.unit |> DistTypes.DistributionUnit.toJson; let timeScale = distPlus.unit |> DistTypes.DistributionUnit.toJson;
<DistributionPlot <DistributionPlot
scale={config.log ? "log" : "linear"}
height={DistPlusPlotReducer.heightToPix(config.height)}
minX minX
maxX maxX
height=80
?continuous ?continuous
color={`hex("5f6b7e")} color={`hex("5f6b7e")}
timeScale timeScale
@ -215,64 +203,94 @@ module IntegralChart = {
}; };
}; };
module Chart = {
[@react.component]
let make = (~distPlus: DistTypes.distPlus, ~config: chartConfig, ~onHover) => {
let chart =
React.useMemo2(
() => {
config.isCumulative
? <IntegralChart distPlus config onHover />
: <DistPlusChart distPlus config onHover />
},
(distPlus, config),
);
<div
className=Css.(
style([
minHeight(`px(DistPlusPlotReducer.heightToPix(config.height))),
])
)>
chart
</div>;
};
};
let button = "bg-gray-300 hover:bg-gray-500 text-grey-darkest text-xs px-4 py-1"; let button = "bg-gray-300 hover:bg-gray-500 text-grey-darkest text-xs px-4 py-1";
[@react.component] [@react.component]
let make = (~distPlus: DistTypes.distPlus) => { let make = (~distPlus: DistTypes.distPlus) => {
let (x, setX) = React.useState(() => 0.); let (x, setX) = React.useState(() => 0.);
let (state, dispatch) = let (state, dispatch) =
React.useReducer( React.useReducer(DistPlusPlotReducer.reducer, DistPlusPlotReducer.init);
(state: state, action: action) =>
switch (action) {
| CHANGE_LOG => {...state, log: !state.log}
| CHANGE_HEIGHT(height) => {...state, height}
| CHANGE_SHOW_STATS => {...state, showStats: !state.showStats}
| CHANGE_SHOW_PARAMS => {...state, showParams: !state.showParams}
},
{log: false, height: 80, showStats: false, showParams: false},
);
let chart =
React.useMemo2(
() => {<DistPlusChart distPlus state onHover={r => {setX(_ => r)}} />},
(distPlus, state),
);
let chart2 =
React.useMemo1(
() => {<IntegralChart distPlus onHover={r => {setX(_ => r)}} />},
[|distPlus|],
);
<div> <div>
chart {state.distributions
chart2 |> E.L.fmapi((index, config) =>
<div className="inline-flex opacity-50 hover:opacity-100"> <div className="flex">
<button className=button onClick={_ => dispatch(CHANGE_LOG)}> <div className="w-4/5">
{(state.log ? "x-Linear" : "x-Log") |> ReasonReact.string} <Chart distPlus config onHover={r => {setX(_ => r)}} />
</div>
<div className="w-1/5">
<div className="opacity-50 hover:opacity-100">
<button
className=button
onClick={_ => dispatch(CHANGE_LOG(index))}>
{(state.log ? "log" : "linear") |> ReasonReact.string}
</button> </button>
<button
className=button
onClick={_ =>
dispatch(
CHANGE_IS_CUMULATIVE(index, !config.isCumulative),
)
}>
{(config.isCumulative ? "cdf" : "pdf") |> ReasonReact.string}
</button>
<button
className=button
onClick={_ => dispatch(HEIGHT_INCREMENT(index))}>
{"Expand" |> ReasonReact.string}
</button>
<button
className=button
onClick={_ => dispatch(HEIGHT_DECREMENT(index))}>
{"Compress" |> ReasonReact.string}
</button>
{index != 0
? <button
className=button
onClick={_ => dispatch(REMOVE_DIST(index))}>
{"Remove" |> ReasonReact.string}
</button>
: ReasonReact.null}
</div>
</div>
</div>
)
|> E.L.toArray
|> ReasonReact.array}
<div className="inline-flex opacity-50 hover:opacity-100">
<button className=button onClick={_ => dispatch(CHANGE_SHOW_STATS)}> <button className=button onClick={_ => dispatch(CHANGE_SHOW_STATS)}>
{"Stats" |> ReasonReact.string} {"Stats" |> ReasonReact.string}
</button> </button>
<button className=button onClick={_ => dispatch(CHANGE_SHOW_PARAMS)}> <button className=button onClick={_ => dispatch(CHANGE_SHOW_PARAMS)}>
{"Params" |> ReasonReact.string} {"Params" |> ReasonReact.string}
</button> </button>
<button <button className=button onClick={_ => dispatch(ADD_DIST)}>
className=button {"Add" |> ReasonReact.string}
onClick={_ => dispatch(CHANGE_HEIGHT(state.height + 40))}>
{"Expand" |> ReasonReact.string}
</button>
<button
className=button
onClick={_ =>
dispatch(
CHANGE_HEIGHT(
state.height < 81 ? state.height : state.height - 40,
),
)
}>
{"Compress" |> ReasonReact.string}
</button> </button>
</div> </div>
{state.showParams ? showAsForm(distPlus) : ReasonReact.null} {state.showParams ? showAsForm(distPlus) : ReasonReact.null}
{state.showStats ? table(distPlus, x) : ReasonReact.null} {state.showStats ? table(distPlus, x) : ReasonReact.null}
</div>; </div>;
// chart
}; };

View File

@ -0,0 +1,97 @@
type chartConfig = {
log: bool,
isCumulative: bool,
height: int,
};
type state = {
log: bool,
showStats: bool,
showParams: bool,
distributions: list(chartConfig),
};
type action =
| CHANGE_SHOW_STATS
| CHANGE_SHOW_PARAMS
| REMOVE_DIST(int)
| ADD_DIST
| CHANGE_LOG(int)
| CHANGE_IS_CUMULATIVE(int, bool)
| HEIGHT_INCREMENT(int)
| HEIGHT_DECREMENT(int);
let changeHeight = (currentHeight, foo: [ | `increment | `decrement]) =>
switch (currentHeight, foo) {
| (1, `decrement) => 1
| (2, `decrement) => 1
| (3, `decrement) => 2
| (4, `decrement) => 3
| (1, `increment) => 2
| (2, `increment) => 3
| (3, `increment) => 4
| (4, `increment) => 4
| _ => 1
};
let heightToPix =
fun
| 1 => 80
| 2 => 140
| 3 => 240
| 4 => 340
| _ => 140;
let distributionReducer = (index, state: list(chartConfig), action) => {
Js.log3(index, action, state);
switch (action, E.L.get(state, index)) {
| (HEIGHT_INCREMENT(_), Some(dist)) =>
E.L.update(
{...dist, height: changeHeight(dist.height, `increment)},
index,
state,
)
| (HEIGHT_DECREMENT(_), Some(dist)) =>
E.L.update(
{...dist, height: changeHeight(dist.height, `decrement)},
index,
state,
)
| (CHANGE_IS_CUMULATIVE(_, isCumulative), Some(dist)) =>
E.L.update({...dist, isCumulative}, index, state)
| (CHANGE_LOG(_), Some(dist)) =>
E.L.update({...dist, log: !dist.log}, index, state)
| (REMOVE_DIST(_), Some(_)) => E.L.remove(index, 1, state)
| (ADD_DIST, Some(_)) =>
E.L.append(state, [{log: false, isCumulative: false, height: 2}])
| _ => state
};
};
let reducer = (state: state, action: action) =>
switch (action) {
| CHANGE_LOG(i)
| CHANGE_IS_CUMULATIVE(i, _)
| HEIGHT_DECREMENT(i)
| REMOVE_DIST(i)
| HEIGHT_INCREMENT(i) => {
...state,
distributions: distributionReducer(i, state.distributions, action),
}
| ADD_DIST => {
...state,
distributions: distributionReducer(0, state.distributions, action),
}
| CHANGE_SHOW_STATS => {...state, showStats: !state.showStats}
| CHANGE_SHOW_PARAMS => {...state, showParams: !state.showParams}
};
let init = {
log: false,
showStats: false,
showParams: false,
distributions: [
{log: false, isCumulative: false, height: 2},
{log: false, isCumulative: true, height: 1},
],
};

View File

@ -177,10 +177,13 @@ module JsDate = {
/* List */ /* List */
module L = { module L = {
let fmap = List.map; let fmap = List.map;
let get = Belt.List.get;
let toArray = Array.of_list; let toArray = Array.of_list;
let fmapi = List.mapi; let fmapi = List.mapi;
let concat = List.concat; let concat = List.concat;
let append = List.append; let append = List.append;
let drop = Rationale.RList.drop;
let remove = Rationale.RList.remove;
let find = List.find; let find = List.find;
let filter = List.filter; let filter = List.filter;
let for_all = List.for_all; let for_all = List.for_all;