Skip to content

Commit

Permalink
Extract Path model
Browse files Browse the repository at this point in the history
  • Loading branch information
vladminsky committed Mar 10, 2016
1 parent b927800 commit 82f2f40
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 49 deletions.
138 changes: 89 additions & 49 deletions src/elements/element.path.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import {CSS_PREFIX} from '../const';
import {default as _} from 'underscore';
import {Element} from './element';
import {PathModel} from '../models/path';
import {elementDecoratorShowText} from './decorators/show-text';
import {elementDecoratorShowAnchors} from './decorators/show-anchors';
import {getLineClassesByCount} from '../utils/css-class-map';
import {default as d3} from 'd3';
import {utils} from '../utils/utils';

export class Path extends Element {

constructor(config) {
Expand Down Expand Up @@ -33,6 +36,13 @@ export class Path extends Element {
});
this.config.guide.color = _.defaults(this.config.guide.color || {}, {fill: null});

this.decorators = [
PathModel.decorator_orientation,
PathModel.decorator_group,
PathModel.decorator_size,
PathModel.decorator_color
];

this.on('highlight', (sender, e) => this.highlight(e));
this.on('highlight-data-points', (sender, e) => this.highlightDataPoints(e));

Expand Down Expand Up @@ -63,6 +73,49 @@ export class Path extends Element {
.regScale('text', this.text);
}

buildModel({xScale, yScale, sizeScale, colorScale, textScale, dataSource}) {

var self = this;
var args = {xScale, yScale, sizeScale, colorScale, dataSource};

var model = this
.decorators
.filter(x => x)
.reduce(((model, transform) => transform(model, args)), (new PathModel()));

return {
scaleX: model.scaleX,
scaleY: model.scaleY,
scaleColor: colorScale,
scaleText: textScale,
x: model.xi,
y: model.yi,
size: model.size,
group: model.group,
color: model.color,
matchRowInCoordinates(rows, {x, y}) {

// d3.invert doesn't work for ordinal axes
var nearest = self
.unpackFrameData(rows)
.map((row) => {
var rx = model.xi(row);
var ry = model.yi(row);
return {
x: rx,
y: ry,
dist: self.getDistance(x, y, rx, ry),
data: row
};
})
.sort((a, b) => (a.dist - b.dist)) // asc
[0];

return nearest.data;
}
};
}

packFrameData(rows) {
return rows;
}
Expand All @@ -82,17 +135,23 @@ export class Path extends Element {
var guide = this.config.guide;
var options = this.config.options;

var xScale = this.xScale;
var yScale = this.yScale;
var colorScale = this.color;
var textScale = this.text;
var fullData = frames.reduce(((memo, f) => memo.concat(f.part())), []);

var model = this.buildModel({
xScale: this.xScale,
yScale: this.yScale,
sizeScale: this.size,
colorScale: this.color,
textScale: this.text
});
this.model = model;

var countCss = getLineClassesByCount(frames.length);

const areaPref = `${CSS_PREFIX}area i-role-element area ${countCss} ${guide.cssClass} `;

var polygonPointsMapper = ((rows) => (rows
.map((d) => [xScale(d[xScale.dim]), yScale(d[yScale.dim])].join(','))
.map((d) => [model.x(d), model.y(d)].join(','))
.join(' ')));

var updateArea = function () {
Expand All @@ -108,48 +167,28 @@ export class Path extends Element {
.attr('points', polygonPointsMapper);

self.subscribe(path, function (rows) {

var m = d3.mouse(this);
var mx = m[0];
var my = m[1];

// d3.invert doesn't work for ordinal axes
var nearest = self
.unpackFrameData(rows)
.map((row) => {
var rx = xScale(row[xScale.dim]);
var ry = yScale(row[yScale.dim]);
return {
x: rx,
y: ry,
dist: self.getDistance(mx, my, rx, ry),
data: row
};
})
.sort((a, b) => (a.dist - b.dist)) // asc
[0];

return nearest.data;
return model.matchRowInCoordinates(rows, {x: m[0], y: m[1]});
});

if (guide.showAnchors && !this.empty()) {

var anch = elementDecoratorShowAnchors({
xScale,
yScale,
xScale: model.scaleX,
yScale: model.scaleY,
guide,
container: this
});

self.subscribe(anch);
}

if (textScale.dim && !this.empty()) {
if (model.scaleText.dim && !this.empty()) {
elementDecoratorShowText({
guide,
xScale,
yScale,
textScale,
xScale: model.scaleX,
yScale: model.scaleY,
textScale: model.scaleText,
container: this
});
}
Expand All @@ -160,10 +199,10 @@ export class Path extends Element {
return function () {

this.attr('class', ({data: f}) =>
`${areaPref} ${colorScale(f.tags[colorScale.dim])} ${x} frame-${f.hash}`)
`${areaPref} ${model.color(f.data[0])} ${x} frame-${f.hash}`)
.call(function () {

if (guide.color.fill && !colorScale.dim) {
if (guide.color.fill && !model.scaleColor.dim) {
this.style({
fill: guide.color.fill,
stroke: guide.color.fill
Expand All @@ -175,22 +214,24 @@ export class Path extends Element {
};
};

var mapper = (f) => {
return {
data: {
tags: f.key || {},
hash: f.hash(),
data: f.part()
},
uid: options.uid
};
};
var groups = _.groupBy(fullData, model.group);
var fibers = Object.keys(groups).reduce(
(memo, k) => {
return memo.concat({
data: {
hash: utils.generateHash(k),
data: groups[k]
},
uid: options.uid
});
},
[]);

var drawFrame = (id) => {

var frameGroups = options.container
.selectAll(`.frame-${id}`)
.data(frames.map(mapper), ({data: f}) => f.hash);
.data(fibers, ({data: f}) => f.hash);
frameGroups
.exit()
.remove();
Expand All @@ -212,21 +253,20 @@ export class Path extends Element {
.container
.selectAll('.area')
.classed({
'graphical-report__highlighted': (({data: d}) => filter(d.tags) === true),
'graphical-report__dimmed': (({data: d}) => filter(d.tags) === false)
'graphical-report__highlighted': (({data: f}) => filter(f.data[0]) === true),
'graphical-report__dimmed': (({data: f}) => filter(f.data[0]) === false)
});
}

highlightDataPoints(filter) {
var colorScale = this.color;
const cssClass = 'i-data-anchor';
this.config
.options
.container
.selectAll(`.${cssClass}`)
.attr({
r: (d) => (filter(d) ? 3 : this.config.guide.anchorSize),
class: (d) => (`${cssClass} ${colorScale(d[colorScale.dim])}`)
class: (d) => (`${cssClass} ${this.model.color(d)}`)
});
}
}
58 changes: 58 additions & 0 deletions src/models/path.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
export class PathModel {

constructor(model = {}) {
var createFunc = ((x) => (() => x));
this.scaleX = model.scaleX || null;
this.scaleY = model.scaleY || null;
this.yi = model.yi || createFunc(0);
this.xi = model.xi || createFunc(0);
this.size = model.size || createFunc(1);
this.color = model.color || createFunc('');
this.group = model.group || createFunc('');
}

static compose(prev, updates = {}) {
return (Object
.keys(updates)
.reduce((memo, propName) => {
memo[propName] = updates[propName];
return memo;
},
(new PathModel(prev))));
}

static decorator_identity(model) {
return PathModel.compose(model);
}

static decorator_orientation(model, {xScale, yScale, isHorizontal = false}) {

var baseScale = (isHorizontal ? yScale : xScale);
var valsScale = (isHorizontal ? xScale : yScale);

return PathModel.compose(model, {
scaleX: baseScale,
scaleY: valsScale,
yi: ((d) => (valsScale(d[valsScale.dim]))),
xi: ((d) => (baseScale(d[baseScale.dim])))
});
}

static decorator_size(model, {sizeScale}) {
return PathModel.compose(model, {
size: ((d) => (sizeScale(d[sizeScale.dim])))
});
}

static decorator_color(model, {colorScale}) {
return PathModel.compose(model, {
color: ((d) => colorScale(d[colorScale.dim]))
});
}

static decorator_group(model, {colorScale}) {
return PathModel.compose(model, {
group: ((d) => (d[colorScale.dim]))
});
}
}

0 comments on commit 82f2f40

Please sign in to comment.