Last active
September 23, 2016 19:42
-
-
Save stormpython/02e0e9495da836b8618fe152607b0da4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
// modules | |
import React from 'react'; | |
import ReactDOM from 'react-dom'; | |
import d3 from 'd3'; | |
import { isNaN } from 'lodash'; | |
import pointsLayout from '../layout/symbols'; | |
let PropTypes = React.PropTypes; | |
class Symbols extends React.Component { | |
constructor(props) { | |
super(props); | |
} | |
_getSymbolLayout(props) { | |
return pointsLayout() | |
.x(props.x) | |
.y(props.y) | |
.radius(props.radius) | |
.size(props.size) | |
.maxRadiusFactor(props.maxRadiusFactor) | |
.groupBy(props.groupBy) | |
.defined(props.defined) | |
.xScale(props.xScale) | |
.yScale(props.yScale) | |
.radiusScale(props.radiusScale); | |
} | |
render() { | |
const symbol = d3.svg.symbol(); | |
const symbolClass = d3.functor(this.props.class); | |
const symbolLayout = this._getSymbolLayout(this.props); | |
return { | |
symbolLayout(this.props.data).map((p, i) => { | |
const symbolType = this.props.type ? | |
d3.functor(this.props.type)(p) : p.coords.symbolType; | |
symbol.size(p.coords.radius).type(symbolType); | |
return <path | |
key={`symbol-${i}`} | |
class={symbolClass(p)} | |
style={this.props.style} | |
d={symbol()} | |
transform={`translate(${p.coords.x},${p.coords.y})`}> | |
</path>; | |
}); | |
} | |
} | |
} | |
// Public API | |
Symbols.propTypes = { | |
class: PropTypes.oneOfType([ | |
PropTypes.string, | |
PropTypes.func | |
]) | |
style: PropTypes.object, | |
data: PropTypes.arrayOf(PropTypes.object), | |
x: PropTypes.oneOfType([ | |
PropTypes.string, | |
PropTypes.func | |
]), | |
y: PropTypes.oneOfType([ | |
PropTypes.string, | |
PropTypes.func | |
]), | |
radius: PropTypes.oneOfType([ | |
PropTypes.number, | |
PropTypes.func | |
]), | |
size: PropTypes.number, | |
maxRadiusFactor: PropTypes.number, | |
type: PropTypes.oneOfType([ | |
PropTypes.string, | |
PropTypes.func | |
]), | |
groupBy: PropTypes.oneOfType([ | |
PropTypes.string, | |
PropTypes.func | |
]), | |
defined: PropTypes.func, | |
xScale: PropTypes.func, | |
yScale: PropTypes.func, | |
radiusScale: PropTypes.func | |
}; | |
Symbols.defaultProps = { | |
class: 'point' | |
data: [], | |
x: (d) => d.x, | |
y: (d) => d.y, | |
radius: 64, | |
size: 64, | |
maxRadiusFactor: 1, | |
defined: (d) => !isNaN(d), | |
xScale: d3.scale.linear(), | |
yScale: d3.scale.linear(), | |
radiusScale: d3.scale.sqrt() | |
}; | |
module.exports = Symbols; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { cloneDeep, first, isFunction, isNumber, uniq } from 'lodash'; | |
import d3 from 'd3'; | |
import valuator from '../shared/valuator'; | |
export default function points() { | |
let x = (d) => d.x; | |
let y = (d) => d.y; | |
let xScale = d3.scale.linear(); | |
let yScale = d3.scale.linear(); | |
let radiusScale = d3.scale.sqrt(); | |
let radius = d3.functor(64); | |
let defined = (d) => !isNaN(d); | |
let groupBy = () => {}; | |
let size = 64; | |
let maxRadiusFactor = 1; | |
function X(d, i) { | |
if (isFunction(xScale.rangeRoundBands)) { | |
return xScale(x.call(this, d, i)) + xScale.rangeBand() / 2; | |
} | |
return xScale(x.call(this, d, i)); | |
} | |
function Y(d, i) { | |
if (isFunction(yScale.rangeRoundBands)) { | |
return yScale(x.call(this, d, i)) + yScale.rangeBand() / 2; | |
} | |
return yScale(y.call(this, d, i)); | |
} | |
function getRadius(d) { | |
return (radius.call(this, d) / Math.PI); | |
} | |
function layout(data) { | |
const filteredData = data | |
.reduce((a, b) => a.concat(b), []) // Merge inner arrays => [[]] to [] | |
.filter((d, i) => defined(x.call(this, d, i)) && defined(y.call(this, d, i))) | |
.sort((a, b) => radiusScale(radius.call(this, b)) - radiusScale(radius.call(this, a))); | |
const symbolScale = d3.scale.ordinal() | |
.domain(uniq(filteredData.map(groupBy))) | |
.range(d3.svg.symbolTypes); | |
radiusScale | |
.domain([ | |
d3.min(filteredData, getRadius), | |
d3.max(filteredData, getRadius), | |
]) | |
.range([size, (size * maxRadiusFactor)]); // 64 is the default | |
// Do not mutate the original data, return a new object | |
return filteredData.map((d, i) => { | |
const datum = cloneDeep(d); | |
datum.coords = { | |
x: X.call(this, d, i), | |
y: Y.call(this, d, i), | |
radius: radiusScale(radius.call(this, d, i)), | |
symbolType: symbolScale.call(this, groupBy(d)), | |
}; | |
return datum; | |
}); | |
} | |
// Public API | |
layout.x = (...args) => { | |
if (!args.length) { return x; } | |
const value = valuator(first(args)); | |
x = isFunction(value) ? value : x; | |
return layout; | |
}; | |
layout.y = (...args) => { | |
if (!args.length) { return y; } | |
const value = valuator(first(args)); | |
y = isFunction(value) ? value : y; | |
return layout; | |
}; | |
layout.radius = (...args) => { | |
if (!args.length) { return radius; } | |
const value = first(args); | |
radius = d3.functor(value); | |
return layout; | |
}; | |
layout.size = (...args) => { | |
if (!args.length) { return size; } | |
const value = first(args); | |
size = isNumber(value) ? value : size; | |
return layout; | |
} | |
layout.maxRadiusFactor = (...args) => { | |
if (!args.length) { return maxRadiusFactor; } | |
const value = first(args); | |
maxRadiusFactor = isNumber(value) ? value : maxRadiusFactor; | |
return layout; | |
}; | |
layout.groupBy = (...args) => { | |
if (!args.length) { return groupBy; } | |
const value = valuator(first(args)); | |
groupBy = isFunction(value) ? value : groupBy; | |
return layout; | |
}; | |
layout.defined = (...args) => { | |
if (!args.length) { return defined; } | |
const value = first(args); | |
defined = isFunction(value) ? value : defined; | |
return layout; | |
}; | |
layout.xScale = (...args) => { | |
if (!args.length) { return xScale; } | |
const value = first(args); | |
xScale = isFunction(value) ? value : xScale; | |
return layout; | |
}; | |
layout.yScale = (...args) => { | |
if (!args.length) { return yScale; } | |
const value = first(args); | |
yScale = isFunction(value) ? value : yScale; | |
return layout; | |
}; | |
layout.radiusScale = (...args) => { | |
if (!args.length) { return radiusScale; } | |
const value = first(args); | |
radiusScale = isFunction(value) ? value : radiusScale; | |
return layout; | |
}; | |
return layout; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
// modules | |
import React from 'react'; | |
import ReactDOM from 'react-dom'; | |
import d3 from 'd3'; | |
import { isUndefined } from 'lodash'; | |
import functor from '../helpers/functor'; | |
let PropTypes = React.PropTypes; | |
class Voronoi extends React.Component { | |
constructor(props) { | |
super(props); | |
} | |
_getVoronoiLayout(props) { | |
const voronoi = d3.geom.voronoi() | |
.x(functor(props.x)) | |
.y(functor(props.y)) | |
.clipExtent(props.clipExtent); | |
if (props.links) { | |
return voronoi.links; | |
} else if (props.triangles) { | |
return voronoi.triangles; | |
} else { | |
return voronoi; | |
} | |
} | |
render() { | |
const voronoiClass = d3.functor(this.props.class); | |
const voronoiLayout = this._getVoronoiLayout(this.props); | |
return { | |
voronoiLayout(this.props.data) | |
.filter(this.props.defined) | |
.map((d, i) => { | |
return <path | |
key={`voronoi-${i}`} | |
class={voronoiClass(d)} | |
style={this.props.style} | |
d={`M${d.join('L')}Z`}> | |
</path>; | |
}); | |
}; | |
} | |
} | |
// Public API | |
Voronoi.propTypes = { | |
class: PropTypes.oneOfType([ | |
PropTypes.string, | |
PropTypes.func | |
]), | |
style: PropTypes.object, | |
data: PropTypes.arrayOf(PropTypes.object), | |
x: PropTypes.oneOfType([ | |
PropTypes.string, | |
PropTypes.number, | |
PropTypes.func | |
]), | |
y: PropTypes.oneOfType([ | |
PropTypes.string, | |
PropTypes.number, | |
PropTypes.func | |
]), | |
clipExtent: PropTypes.oneOfType([ | |
PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)), | |
PropTypes.object // null ? does this work??? | |
]) | |
defined: PropTypes.func, | |
links: PropTypes.bool, | |
triangles: PropTypes.bool | |
}; | |
Voronoi.defaultProps = { | |
class: 'point', | |
data: [], | |
x: (d) => d.x, | |
y: (d) => d.y, | |
clipExtent: null, | |
defined: (d) => !isUndefined(d), | |
links: false, | |
traingles: false | |
}; | |
module.exports = Voronoi; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment