aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/About.tsx10
-rw-r--r--src/Grid.tsx48
-rw-r--r--src/Snow.tsx268
-rw-r--r--src/index.html2
-rw-r--r--src/index.tsx2
5 files changed, 307 insertions, 23 deletions
diff --git a/src/About.tsx b/src/About.tsx
index 9aae40c..118b961 100644
--- a/src/About.tsx
+++ b/src/About.tsx
@@ -60,16 +60,6 @@ function About(props: AboutProps) {
73d at tedyin dot com
</ListItem>
<ListItem>
- <span className={classes.infoField}>GitHub:</span>
- <Link href="https://github.com/Determinant/chromicle" target="_blank" rel="noopener">
- Determinant/chromicle
- </Link>
- </ListItem>
- <ListItem>
- <span className={classes.infoField}>About Me:</span>
- <Link href="https://www.cs.cornell.edu/~tedyin/" target="_blank" rel="noopener">
- https://www.cs.cornell.edu/~tedyin/
- </Link>
</ListItem>
<ListItem>
<span className={classes.infoField}>Buy me a cup of coffee:</span>
diff --git a/src/Grid.tsx b/src/Grid.tsx
index 65cf2ec..f68c550 100644
--- a/src/Grid.tsx
+++ b/src/Grid.tsx
@@ -1,17 +1,57 @@
import React from 'react';
import 'typeface-roboto';
import MGrid from '@material-ui/core/Grid';
+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';
+const styles = (theme: Theme): StyleRules => ({
+ gridRow: {
+ height: 24,
+ textAlign: 'center'
+ },
+ gridCell: {
+ height: 20,
+ width: 20,
+ margin: 2,
+ display: 'inline-block'
+ }
+});
interface GridProps {
- classes: {}
+ classes: {
+ gridRow: string,
+ gridCell: string
+ },
+ data: {d: number[], col: number}[][]
}
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();
+ }
render() {
- const { classes } = this.props;
- return (<div></div>);
+ const { classes, data } = this.props;
+ return (
+ <div style={{margin: '0 auto'}}>
+ {
+ data.map((row, i) => (
+ <div key={i} className={classes.gridRow}>
+ {
+ row.map((cell, j) => (
+ <div key={j} className={classes.gridCell} style={{backgroundColor: Grid.getColor(cell)}}>
+ </div>
+ ))
+ }
+ </div>
+ ))
+ }
+ </div>
+ );
}
}
-export default Grid;
+export default withStyles(styles)(Grid);
diff --git a/src/Snow.tsx b/src/Snow.tsx
index 47a1c06..9ab4dfd 100644
--- a/src/Snow.tsx
+++ b/src/Snow.tsx
@@ -1,23 +1,277 @@
import React from 'react';
import MGrid from '@material-ui/core/Grid';
-import { Theme, withStyles } from '@material-ui/core/styles';
+import { Theme, withStyles, StyleRules } from '@material-ui/core/styles';
+import TextField from '@material-ui/core/TextField';
+import Table from '@material-ui/core/Table';
+import TableBody from '@material-ui/core/TableBody';
+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';
-interface SnowProps {
- classes: {}
-}
-
-const styles = (theme: Theme) => ({
+const styles = (theme: Theme): StyleRules => ({
+ inputLabel: {
+ fontSize: 16,
+ paddingRight: 0,
+ textAlign: 'right'
+ },
+ inputValue: {
+ textAlign: 'left'
+ },
buttonSpacer: {
marginBottom: theme.spacing.unit * 4,
},
+ bottomButtons: {
+ marginTop: 10,
+ textAlign: 'center',
+ minWidth: 650
+ },
+ slider: {
+ padding: '22px 0px',
+ }
});
+interface SnowProps {
+ classes: {
+ inputLabel: string,
+ inputValue: string,
+ buttonSpacer: string,
+ bottomButtons: string,
+ slider: string
+ }
+}
+
+function getRandomInt(max: number) {
+ return Math.floor(Math.random() * Math.floor(max));
+}
+
+function getRandomSubarray(arr: number[], size: number) {
+ let shuffled = arr.slice(0), i = arr.length, min = i - size, temp, index;
+ while (i-- > min) {
+ index = Math.floor((i + 1) * Math.random());
+ temp = shuffled[index];
+ shuffled[index] = shuffled[i];
+ shuffled[i] = temp;
+ }
+ return shuffled.slice(min);
+}
+
class Snow extends React.Component<SnowProps> {
+
+ static genMatrix(n = 20) {
+ let d = [];
+ for (let i = 0; i < n; i++)
+ {
+ let r = [];
+ for (let j = 0; j < n; j++)
+ r.push({d: [0, 0], col: getRandomInt(2)});
+ d.push(r);
+ }
+ return d;
+ }
+
+ state = {
+ colorMatrix: Snow.genMatrix(),
+ k: 10,
+ alpha: 8,
+ nodesPerTick: 10,
+ maxInactiveTicks: 200,
+ ticking: false,
+ simulationSpeed: 100
+ };
+
+ config = {
+ k: 10,
+ alpha: 8,
+ nodesPerTick: 10,
+ maxInactiveTicks: 200,
+ inactiveTicks: 0
+ };
+
+ getNodeState(n: number, u: number) {
+ let r = Math.floor(u / n);
+ let c = u % n;
+ return this.state.colorMatrix[r][c];
+ }
+
+ setNodeState(n: number, u: number, s: {d: number[], col: number}) {
+ let r = Math.floor(u / n);
+ let c = u % n;
+ let m = [...this.state.colorMatrix];
+ m[r][c] = s;
+ this.setState({colorMatrix: m});
+ }
+
+ tick(n: number, m: number) {
+ let N = n * n;
+ let active = false;
+ for (let i = 0; i < m; i++)
+ {
+ let u = getRandomInt(N);
+ let peers = [];
+ for (let j = 0; j < N; j++)
+ if (j != u) peers.push(j);
+ let sample = getRandomSubarray(peers, this.config.k);
+ let cnt = [0, 0];
+ sample.forEach((v) => {
+ let ss = this.getNodeState(n, v);
+ cnt[ss.col]++;
+ });
+ let s = this.getNodeState(n, u);
+ for (let c = 0; c < 2; c++)
+ {
+ if (cnt[c] > this.config.alpha)
+ {
+ s.d[c]++;
+ if (s.d[c] > s.d[s.col])
+ {
+ if (s.col != c) active = true;
+ s.col = c;
+ this.setNodeState(n, u, s);
+ }
+ }
+ }
+ }
+ return active;
+ }
+
+ pauseTick() {
+ this.setState({ticking: false});
+ }
+
+ startTick() {
+ this.config.alpha = this.state.alpha;
+ this.config.k = this.state.k;
+ this.config.nodesPerTick = this.state.nodesPerTick;
+ this.config.inactiveTicks = 0;
+ this.config.maxInactiveTicks = this.state.maxInactiveTicks;
+ this.autoTick();
+ }
+
+ autoTick() {
+ this.setState({ticking: true});
+ setTimeout(() => {
+ let active = this.tick(20, this.config.nodesPerTick);
+ console.log(active);
+ if (!active)
+ {
+ if (++this.config.inactiveTicks > this.config.maxInactiveTicks)
+ {
+ this.pauseTick();
+ return;
+ }
+ }
+ else
+ this.config.inactiveTicks = 0;
+ if (this.state.ticking) this.autoTick();
+ }, 1000 / this.state.simulationSpeed);
+ }
+
+ reset() {
+ this.setState({
+ colorMatrix: Snow.genMatrix(),
+ ticking: false
+ });
+ }
+
render() {
const { classes } = this.props;
return (
- <MGrid container spacing={16} style={{minWidth: 700}}>
+ <MGrid container spacing={16} style={{minWidth: 600, maxWidth: 1600}}>
+ <MGrid item lg={6} xs={12}>
+ <Grid data={this.state.colorMatrix} />
+ </MGrid>
+ <MGrid item lg={6} xs={12}>
+ <Table>
+ <TableBody>
+ <TableRow>
+ <TableCell className={classes.inputLabel}>
+ k =
+ </TableCell>
+ <TableCell>
+ <TextField
+ inputProps={{ className: classes.inputValue } as React.CSSProperties}
+ value={this.state.k}
+ disabled={this.state.ticking}
+ onChange={event => this.setState({k: event.target.value})}/>
+ </TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className={classes.inputLabel}>
+ alpha =
+ </TableCell>
+ <TableCell>
+ <TextField
+ inputProps={{ className: classes.inputValue } as React.CSSProperties}
+ value={this.state.alpha}
+ disabled={this.state.ticking}
+ onChange={event => this.setState({alpha: event.target.value})}/>
+ </TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className={classes.inputLabel}>
+ nodesPerTick =
+ </TableCell>
+ <TableCell>
+ <TextField
+ inputProps={{ className: classes.inputValue } as React.CSSProperties}
+ value={this.state.nodesPerTick}
+ disabled={this.state.ticking}
+ onChange={event => this.setState({nodesPerTick: event.target.value})}/>
+ </TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className={classes.inputLabel}>
+ maxInactiveTicks =
+ </TableCell>
+ <TableCell>
+ <TextField
+ inputProps={{ className: classes.inputValue } as React.CSSProperties}
+ value={this.state.maxInactiveTicks}
+ disabled={this.state.ticking}
+ onChange={event => this.setState({maxInactiveTicks: event.target.value})}/>
+ </TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className={classes.inputLabel}>
+ simulationSpeed
+ </TableCell>
+ <TableCell>
+ <Slider
+ classes={{ container: classes.slider }}
+ value={this.state.simulationSpeed}
+ min={1}
+ max={1000}
+ onChange={(_, value) => this.setState({simulationSpeed: value})} />
+ </TableCell>
+ </TableRow>
+ </TableBody>
+ </Table>
+ <div className={classes.buttonSpacer} />
+ <div className={classes.bottomButtons}>
+ <MGrid container spacing={16}>
+ <MGrid item lg={4} xs={12}>
+ <Button
+ variant="contained" color="primary"
+ onClick={event => this.startTick()}
+ disabled={this.state.ticking}>Start</Button>
+ </MGrid>
+ <MGrid item lg={4} xs={12}>
+ <Button
+ variant="contained" color="primary"
+ onClick={event => this.pauseTick()}
+ disabled={!this.state.ticking}>Pause</Button>
+ </MGrid>
+ <MGrid item lg={4} xs={12}>
+ <Button
+ variant="contained" color="primary"
+ onClick={event => this.reset()}>Reset</Button>
+ </MGrid>
+ </MGrid>
+ </div>
+ </MGrid>
</MGrid>
);
}
diff --git a/src/index.html b/src/index.html
index a51779c..708c4fe 100644
--- a/src/index.html
+++ b/src/index.html
@@ -8,7 +8,7 @@
/>
<meta name="theme-color" content="#000000" />
<link rel="stylesheet" href="/fonts/TypoPRO-FantasqueSansMono-Regular.css" />
- <title>Chromicle</title>
+ <title>Snow</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 da751ca..ad08a87 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -125,7 +125,7 @@ class MainTabs extends React.Component<MainTabsProps> {
{console.log(location)}
<Route exact path="/snow" component={Snow} />
<Route exact path="/about" component={About} />
- <Route exact path="/" render={() => <Redirect to="/settings" />}/>
+ <Route exact path="/" render={() => <Redirect to="/snow" />}/>
</Switch>
</div>
</CSSTransition>