aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDeterminant <ted.sybil@gmail.com>2019-04-10 01:09:41 -0400
committerDeterminant <ted.sybil@gmail.com>2019-04-10 01:09:41 -0400
commit291895a75856408788925bfb34e4c085c40efbaf (patch)
tree4fe1ea3e1b1faf5012de93104c49032dc3937a52 /src
parent94330abf2d1ba606976ec4f2dc10277b78cdd036 (diff)
finish version 1.0
Diffstat (limited to 'src')
-rw-r--r--src/Grid.css35
-rw-r--r--src/Grid.tsx35
-rw-r--r--src/Logo.tsx12
-rw-r--r--src/Snow.tsx220
-rw-r--r--src/index.html3
-rw-r--r--src/index.tsx4
6 files changed, 258 insertions, 51 deletions
diff --git a/src/Grid.css b/src/Grid.css
new file mode 100644
index 0000000..a9c7802
--- /dev/null
+++ b/src/Grid.css
@@ -0,0 +1,35 @@
+div.grid {
+ display: inline-block;
+}
+
+
+.grid div {
+ -webkit-transition: background-color 0.1s ease-out;
+ -moz-transition: background-color 0.1s ease-out;
+ -o-transition: background-color 0.1s ease-out;
+ transition: background-color 0.1s ease-out;
+}
+
+.gridCell > div > div {
+ height: 20px;
+ width: 20px;
+ margin: 2px;
+ display: inline-block;
+}
+
+.smallGridCell > div > div {
+ height: 15px;
+ width: 15px;
+ margin: 1px;
+ display: inline-block;
+}
+
+.gridRow > div {
+ height: 24px;
+ text-align: center;
+}
+
+.smallGridRow > div {
+ height: 17px;
+ text-align: center;
+}
diff --git a/src/Grid.tsx b/src/Grid.tsx
index f68c550..995e59f 100644
--- a/src/Grid.tsx
+++ b/src/Grid.tsx
@@ -5,44 +5,43 @@ import orange from '@material-ui/core/colors/orange';
import blue from '@material-ui/core/colors/blue';
import { Theme, withStyles, StyleRules } from '@material-ui/core/styles';
import Color from 'color';
+import { TransitionGroup, CSSTransition } from "react-transition-group";
+import './Grid.css';
const styles = (theme: Theme): StyleRules => ({
- gridRow: {
- height: 24,
- textAlign: 'center'
- },
- gridCell: {
- height: 20,
- width: 20,
- margin: 2,
- display: 'inline-block'
- }
});
interface GridProps {
classes: {
- gridRow: string,
- gridCell: string
},
data: {d: number[], col: number}[][]
}
+export function getNodeColor(d: number, col: number) {
+ const color = [orange[300], blue[300]];
+ let base = Color(color[col]).hsl().array();
+ base[2] = 80;
+ return Color.hsl(base).darken(Math.min(d / 15, 0.5)).hex();
+}
+
class Grid extends React.Component<GridProps> {
static getColor(s: {d: number[], col: number}) {
- const color = [orange[300], blue[300]];
- let {d, col} = s;
- return Color(color[col]).darken(Math.min(d[col] / 10, 0.6)).hex();
+ return getNodeColor(s.d[s.col], s.col);
}
render() {
const { classes, data } = this.props;
+ const { gr, gc } = data.length <= 20 ?
+ { gr: 'gridRow', gc: 'gridCell' } :
+ { gr: 'smallGridRow', gc: 'smallGridCell' };
return (
- <div style={{margin: '0 auto'}}>
+ <div className={`grid ${gr} ${gc}`}>
{
data.map((row, i) => (
- <div key={i} className={classes.gridRow}>
+ <div key={i}>
{
row.map((cell, j) => (
- <div key={j} className={classes.gridCell} style={{backgroundColor: Grid.getColor(cell)}}>
+ <div key={j}
+ style={{backgroundColor: Grid.getColor(cell)}}>
</div>
))
}
diff --git a/src/Logo.tsx b/src/Logo.tsx
new file mode 100644
index 0000000..1bac2b7
--- /dev/null
+++ b/src/Logo.tsx
@@ -0,0 +1,12 @@
+import React from 'react';
+export default (props: {style: {[key: string]: string | number }}) =>
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 97.079997 37.00"
+ version="1.1"
+ style={props.style}>
+<path
+ d="M 20.52,0 0,36.02 H 14.85 L 28.03,13.19 Z M 13.7,34.02 H 3.45 l 17.08,-29.98 5.21,9.14 z m 8.79,2 H 41.04 L 31.82,19.85 Z m 3.46,-2 5.86,-10.16 5.79,10.16 z M 76.57,0 56.05,36.02 H 70.9 L 84.08,13.19 Z M 69.74,34.02 H 59.49 L 76.57,4.04 81.78,13.18 Z M 87.87,19.84 78.53,36.01 h 18.55 z m -0.01,4.02 5.79,10.16 H 82 Z M 42.92,0 H 28.07 L 48.59,36.02 56.1,22.84 Z M 31.51,2 h 10.25 l 12.03,20.84 -5.21,9.14 z M 69.11,0 H 50.56 L 59.9,16.17 Z M 54.02,2 h 11.65 l -5.79,10.16 z"
+ id="path4"
+ style={{fill:'#f5f1ee'}} />
+</svg>
diff --git a/src/Snow.tsx b/src/Snow.tsx
index a391f0a..89c5073 100644
--- a/src/Snow.tsx
+++ b/src/Snow.tsx
@@ -8,13 +8,17 @@ import TableRow from '@material-ui/core/TableRow';
import TableCell from '@material-ui/core/TableCell';
import Button from '@material-ui/core/Button';
import Slider from '@material-ui/lab/Slider';
-import Grid from './Grid';
+import FormGroup from '@material-ui/core/FormGroup';
+import Grid, {getNodeColor} from './Grid';
+import { Line } from 'react-chartjs-2';
+import Color from 'color';
const styles = (theme: Theme): StyleRules => ({
inputLabel: {
fontSize: 16,
paddingRight: 0,
- textAlign: 'right'
+ textAlign: 'right',
+ width: '30%'
},
inputValue: {
textAlign: 'left'
@@ -28,6 +32,15 @@ const styles = (theme: Theme): StyleRules => ({
},
slider: {
padding: '22px 0px',
+ },
+ errorHint: {
+ fontSize: 16,
+ paddingLeft: 16,
+ lineHeight: '32px',
+ color: theme.palette.secondary.main
+ },
+ grid: {
+ textAlign: 'center'
}
});
@@ -37,7 +50,9 @@ interface SnowProps {
inputValue: string,
buttonSpacer: string,
bottomButtons: string,
- slider: string
+ slider: string,
+ errorHint: string,
+ grid: string
}
}
@@ -56,9 +71,11 @@ function getRandomSubarray(arr: number[], size: number) {
return shuffled.slice(min);
}
+const watchedD = [15, 10, 5, 1];
+
class Snow extends React.Component<SnowProps> {
- static genMatrix(n = 20) {
+ static genMatrix(n: number) {
let d = [];
for (let i = 0; i < n; i++)
{
@@ -71,16 +88,31 @@ class Snow extends React.Component<SnowProps> {
}
state = {
- colorMatrix: Snow.genMatrix(),
- k: 10,
- alpha: 8,
- nodesPerTick: 10,
- maxInactiveTicks: 200,
+ colorMatrix: Snow.genMatrix(20),
+ n: '20',
+ k: '10',
+ alpha: '8',
+ nodesPerTick: '20',
+ maxInactiveTicks: '200',
+ loaded: true,
ticking: false,
- simulationSpeed: 100
+ simulationSpeed: 100,
+ dialogOpen: false,
+ dialogMsg: {title: '', message: ''},
+ nError: false,
+ kError: false,
+ alphaError: false,
+ nodesPerTickError: false,
+ maxInactiveTicksError: false,
+ dcnts: [watchedD.map(() => [] as number[]),
+ watchedD.map(() => [] as number[])] as number[][][],
+ ticks: [] as number[],
+ N: 400
};
config = {
+ iter: 0,
+ n: 20,
k: 10,
alpha: 8,
nodesPerTick: 10,
@@ -105,8 +137,10 @@ class Snow extends React.Component<SnowProps> {
tick(n: number, m: number) {
let N = n * n;
let active = false;
- for (let i = 0; i < m; i++)
- {
+
+ let nodes = [];
+ for (let j = 0; j < N; j++) nodes.push(j);
+ getRandomSubarray(nodes, m).forEach(v => {
let u = getRandomInt(N);
let peers = [];
for (let j = 0; j < N; j++)
@@ -131,6 +165,30 @@ class Snow extends React.Component<SnowProps> {
}
}
}
+ });
+
+ if (this.config.iter % 10 == 0)
+ {
+ let dcnts = [];
+ for (let c = 0; c < 2; c++)
+ {
+ dcnts.push(watchedD.map((d, i) => {
+ let dcnt = 0;
+ for (let i = 0; i < n; i++)
+ for (let j = 0; j < n; j++)
+ {
+ const s = this.state.colorMatrix[i][j];
+ if (s.d[c] >= d)
+ dcnt++;
+ }
+ if (c == 0) dcnt = -dcnt;
+ return [...this.state.dcnts[c][i], dcnt].splice(-50);
+ }));
+ }
+ this.setState({
+ dcnts: dcnts,
+ ticks: [...this.state.ticks, this.config.iter].splice(-50)
+ });
}
return active;
}
@@ -140,19 +198,65 @@ class Snow extends React.Component<SnowProps> {
}
startTick() {
- this.config.alpha = this.state.alpha;
- this.config.k = this.state.k;
- this.config.nodesPerTick = this.state.nodesPerTick;
+ const n = Number(this.state.n);
+ const N = n * n;
+ const k = Number(this.state.k);
+ const alpha = Number(this.state.alpha);
+ const nodesPerTick = Number(this.state.nodesPerTick);
+ const maxInactiveTicks = Number(this.state.maxInactiveTicks);
+
+ if (!Number.isInteger(n) || n < 2 || n > 40)
+ {
+ this.setState({ nError: true });
+ return;
+ }
+ if (!Number.isInteger(k) || k < 1 || k > N)
+ {
+ this.setState({ kError: true });
+ return;
+ }
+ if (!Number.isInteger(alpha) || !(k / 2 < alpha && alpha <= k))
+ {
+ this.setState({ alphaError: true });
+ return;
+ }
+ if (!Number.isInteger(nodesPerTick) || nodesPerTick < 1 || nodesPerTick > N)
+ {
+ this.setState({ nodesPerTickError: true });
+ return;
+ }
+ if (!Number.isInteger(maxInactiveTicks) || maxInactiveTicks < 1 || maxInactiveTicks > 1e6)
+ {
+ this.setState({ maxInactiveTicksError: true });
+ return;
+ }
+
+ if (!this.state.loaded)
+ {
+ this.config.iter = 0;
+ this.config.n = n;
+ this.setState({
+ loaded: true,
+ colorMatrix: Snow.genMatrix(this.config.n),
+ dcnts: [watchedD.map(() => [] as number[]),
+ watchedD.map(() => [] as number[])],
+ ticks: [],
+ N: n * n
+ });
+ }
+ this.config.alpha = alpha;
+ this.config.k = k;
+ this.config.nodesPerTick = nodesPerTick;
this.config.inactiveTicks = 0;
- this.config.maxInactiveTicks = this.state.maxInactiveTicks;
+ this.config.maxInactiveTicks = maxInactiveTicks;
this.autoTick();
}
autoTick() {
this.setState({ticking: true});
setTimeout(() => {
- let active = this.tick(20, this.config.nodesPerTick);
- console.log(active);
+ let active = this.tick(this.config.n, this.config.nodesPerTick);
+ this.config.iter++;
if (!active)
{
if (++this.config.inactiveTicks > this.config.maxInactiveTicks)
@@ -169,8 +273,8 @@ class Snow extends React.Component<SnowProps> {
reset() {
this.setState({
- colorMatrix: Snow.genMatrix(),
- ticking: false
+ ticking: false,
+ loaded: false
});
}
@@ -179,22 +283,60 @@ class Snow extends React.Component<SnowProps> {
return (
<MGrid container spacing={16} style={{minWidth: 600}}>
- <MGrid item lg={6} xs={12}>
+ <MGrid item lg={6} xs={12} className={classes.grid}>
<Grid data={this.state.colorMatrix} />
+ <Line data={() => {
+ let datasets = this.state.dcnts.map((dd, c) => dd.map((line, i) => {
+ const base = getNodeColor(watchedD[i], c);
+ return {
+ data: line,
+ label: `${c == 0 ? 'A' : 'B'}(d-${watchedD[i]})`,
+ borderColor: base,
+ backgroundColor: Color(base).fade(0.5).rgb().string(),
+ borderWidth: 2
+ };
+ })).flat();
+ return {
+ datasets,
+ labels: this.state.ticks
+ }
+ }}
+ options={{ scales: { yAxes: [{ ticks: {min: -this.state.N, max: this.state.N}}]}}}/>
</MGrid>
<MGrid item lg={4} xs={12}>
<Table>
<TableBody>
<TableRow>
<TableCell className={classes.inputLabel}>
+ n =
+ </TableCell>
+ <TableCell>
+ <TextField
+ inputProps={{ className: classes.inputValue, maxLength: 2 } as React.CSSProperties}
+ value={this.state.n}
+ disabled={this.state.loaded}
+ style={{width: 40}}
+ error={this.state.nError}
+ onChange={event => this.setState({n: event.target.value, nError: false})}/>
+ <sup>2</sup>
+ {this.state.nError &&
+ <span className={classes.errorHint}>n must be in 2..40</span>}
+ </TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className={classes.inputLabel}>
k =
</TableCell>
<TableCell>
<TextField
- inputProps={{ className: classes.inputValue } as React.CSSProperties}
+ inputProps={{ className: classes.inputValue, maxLength: 4 } as React.CSSProperties}
value={this.state.k}
disabled={this.state.ticking}
- onChange={event => this.setState({k: event.target.value})}/>
+ style={{width: 40}}
+ error={this.state.kError}
+ onChange={event => this.setState({k: event.target.value, kError: false})}/>
+ {this.state.kError &&
+ <span className={classes.errorHint}>k must be in 1..n</span>}
</TableCell>
</TableRow>
<TableRow>
@@ -203,10 +345,14 @@ class Snow extends React.Component<SnowProps> {
</TableCell>
<TableCell>
<TextField
- inputProps={{ className: classes.inputValue } as React.CSSProperties}
+ inputProps={{ className: classes.inputValue, maxLength: 4 } as React.CSSProperties}
value={this.state.alpha}
disabled={this.state.ticking}
- onChange={event => this.setState({alpha: event.target.value})}/>
+ style={{width: 40}}
+ error={this.state.alphaError}
+ onChange={event => this.setState({alpha: event.target.value, alphaError: false})}/>
+ {this.state.alphaError &&
+ <span className={classes.errorHint}>alpha must be in (k/2, k]</span>}
</TableCell>
</TableRow>
<TableRow>
@@ -215,10 +361,14 @@ class Snow extends React.Component<SnowProps> {
</TableCell>
<TableCell>
<TextField
- inputProps={{ className: classes.inputValue } as React.CSSProperties}
+ inputProps={{ className: classes.inputValue, maxLength: 4 } as React.CSSProperties}
value={this.state.nodesPerTick}
disabled={this.state.ticking}
- onChange={event => this.setState({nodesPerTick: event.target.value})}/>
+ style={{width: 40}}
+ error={this.state.nodesPerTickError}
+ onChange={event => this.setState({nodesPerTick: event.target.value, nodesPerTickError: false})}/>
+ {this.state.nodesPerTickError &&
+ <span className={classes.errorHint}>nodesPerTick must be in 1..n</span>}
</TableCell>
</TableRow>
<TableRow>
@@ -230,7 +380,11 @@ class Snow extends React.Component<SnowProps> {
inputProps={{ className: classes.inputValue } as React.CSSProperties}
value={this.state.maxInactiveTicks}
disabled={this.state.ticking}
- onChange={event => this.setState({maxInactiveTicks: event.target.value})}/>
+ style={{width: 50}}
+ error={this.state.maxInactiveTicksError}
+ onChange={event => this.setState({maxInactiveTicks: event.target.value, maxInactiveTicksError: false})}/>
+ {this.state.maxInactiveTicksError &&
+ <span className={classes.errorHint}>maxInactiveTicks must be in 1..1000000</span>}
</TableCell>
</TableRow>
<TableRow>
@@ -252,21 +406,27 @@ class Snow extends React.Component<SnowProps> {
<div className={classes.bottomButtons}>
<MGrid container item spacing={16}>
<MGrid item lg={4} xs={12}>
+ <FormGroup>
<Button
variant="contained" color="primary"
onClick={event => this.startTick()}
- disabled={this.state.ticking}>Start</Button>
+ disabled={this.state.ticking}>Run</Button>
+ </FormGroup>
</MGrid>
<MGrid item lg={4} xs={12}>
+ <FormGroup>
<Button
variant="contained" color="primary"
onClick={event => this.pauseTick()}
- disabled={!this.state.ticking}>Pause</Button>
+ disabled={!this.state.ticking}>Stop</Button>
+ </FormGroup>
</MGrid>
<MGrid item lg={4} xs={12}>
+ <FormGroup>
<Button
variant="contained" color="primary"
onClick={event => this.reset()}>Reset</Button>
+ </FormGroup>
</MGrid>
</MGrid>
</div>
diff --git a/src/index.html b/src/index.html
index 708c4fe..ef3bba6 100644
--- a/src/index.html
+++ b/src/index.html
@@ -7,8 +7,7 @@
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
- <link rel="stylesheet" href="/fonts/TypoPRO-FantasqueSansMono-Regular.css" />
- <title>Snow</title>
+ <title>Snow BFT Demo</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
diff --git a/src/index.tsx b/src/index.tsx
index ad08a87..baa46f5 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -16,6 +16,7 @@ import { TransitionGroup, CSSTransition } from "react-transition-group";
import { theme } from './theme';
import Snow from './Snow';
import About from './About';
+import Logo from './Logo';
const styles = (theme: Theme) => ({
root: {
@@ -96,7 +97,8 @@ class MainTabs extends React.Component<MainTabsProps> {
className={classes.appBar}>
<Toolbar className={classes.toolbar}>
<Typography component="h1" variant="h6" color="inherit" noWrap className={classes.title}>
- Snow
+ <Logo style={{width: '5em', verticalAlign: 'bottom', marginRight: '0.5em'}}/>
+ Snow BFT Demo
</Typography>
<Tabs
classes={{ indicator: classes.indicator }}