Skip to content

Instantly share code, notes, and snippets.

@stormpython
Last active September 23, 2016 19:42
Show Gist options
  • Save stormpython/02e0e9495da836b8618fe152607b0da4 to your computer and use it in GitHub Desktop.
Save stormpython/02e0e9495da836b8618fe152607b0da4 to your computer and use it in GitHub Desktop.
'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;
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;
}
'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