Skip to content

Commit

Permalink
andredumas#38: [API CHANGE] Rendering optimisations to reduce the DOM…
Browse files Browse the repository at this point in the history
… count. As a result volumeOpacity support for candlestick has been REMOVED
  • Loading branch information
andredumas committed Dec 31, 2014
1 parent a37600d commit 467f67c
Show file tree
Hide file tree
Showing 17 changed files with 180 additions and 197 deletions.
155 changes: 85 additions & 70 deletions dist/techan.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/*
TechanJS v0.5.0-4
TechanJS v0.5.0-5
(c) 2014 - 2014 Andre Dumas | https://github.com/andredumas/techan.js
*/
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.techan=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';module.exports='0.5.0-4';
'use strict';module.exports='0.5.0-5';
},{}],2:[function(require,module,exports){
'use strict';

Expand Down Expand Up @@ -1185,41 +1185,37 @@ function backgroundPath(accessor, axis, height, width, point, neg) {
module.exports = function(d3_scale_linear, d3_extent, accessor_ohlc, plot, plotMixin) { // Injected dependencies
return function() { // Closure constructor
var p = {}, // Container for private, direct access mixed in variables
volumeOpacity = false;
bodyPathGenerator,
wickGenerator;

function candlestick(g) {
var group = plot.groupSelect(g, plot.dataMapper.unity, p.accessor.d);
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d),
upDownEqual = plot.groupUpDownEqual(g.datum(), p.accessor);

// Two path's as wick and body can be styled slightly differently (stroke and fills)
group.entry.append('path').attr('class', 'candle body');
group.entry.append('path').attr('class', 'candle wick');
// 3x2 path's as wick and body can be styled slightly differently (stroke and fills)
plot.appendUpDownEqual(group.selection, p.accessor, ['candle', 'body'], upDownEqual);
plot.appendUpDownEqual(group.selection, p.accessor, ['candle', 'wick'], upDownEqual);

candlestick.refresh(g);
}

candlestick.refresh = function(g) {
if(volumeOpacity) opacity(g, d3_scale_linear, d3_extent, p.accessor.v);
refresh(g, plot, p.accessor, p.xScale, p.yScale);
g.selectAll('path.candle.body').attr('d', bodyPathGenerator);
g.selectAll('path.candle.wick').attr('d', wickGenerator);
};

candlestick.volumeOpacity = function(_) {
if (!arguments.length) return volumeOpacity;
volumeOpacity = _;
return candlestick;
};
function binder() {
bodyPathGenerator = plot.joinPath(p.accessor, p.xScale, p.yScale, bodyPath);
wickGenerator = plot.joinPath(p.accessor, p.xScale, p.yScale, wickPath);
}

// Mixin 'superclass' methods and variables
plotMixin(candlestick, p).plot(accessor_ohlc());
plotMixin(candlestick, p).plot(accessor_ohlc(), binder);

return candlestick;
};
};

function refresh(g, plot, accessor, x, y) {
g.selectAll('path.candle.body').attr('d', bodyPath(accessor, x, y)).classed(plot.classedUpDown(accessor));
g.selectAll('path.candle.wick').attr('d', wickPath(accessor, x, y)).classed(plot.classedUpDown(accessor));
}

function bodyPath(accessor, x, y) {
return function(d) {
var path = [],
Expand Down Expand Up @@ -1277,18 +1273,6 @@ function wickPath(accessor, x, y) {
return path.join(' ');
};
}

function opacity(g, d3_scale_linear, d3_extent, accessor_volume) {
var selection = g.selectAll('g.data'),
volumeOpacityScale = d3_scale_linear()
.domain(d3_extent(selection.data().map(accessor_volume).filter(function(d) { return !isNaN(d); })))
.range([0.2, 1]);

selection.selectAll('path.candle').style('opacity', function(d) {
var volume = accessor_volume(d);
return isNaN(volume) ? null : volumeOpacityScale(volume);
});
}
},{}],23:[function(require,module,exports){
'use strict';

Expand Down Expand Up @@ -1610,19 +1594,14 @@ function refresh(g, accessor, x, y, plot, svgLine, showZero) {
module.exports = function(accessor_macd, plot, plotMixin) { // Injected dependencies
return function() { // Closure function
var p = {}, // Container for private, direct access mixed in variables
differenceGenerator,
macdLine = plot.pathLine(),
signalLine = plot.pathLine();

function macd(g) {
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d);

var histogramSelection = group.selection
.append('g').attr('class', 'difference')
.selectAll('g.difference').data(function(data) { return data; });

histogramSelection.exit().remove();
histogramSelection.enter().append('path').attr('class', 'difference');

group.selection.append('path').attr('class', 'difference');
group.selection.append('path').attr('class', 'zero');
group.selection.append('path').attr('class', 'macd');
group.selection.append('path').attr('class', 'signal');
Expand All @@ -1631,10 +1610,11 @@ module.exports = function(accessor_macd, plot, plotMixin) { // Injected depende
}

macd.refresh = function(g) {
refresh(g, p.accessor, p.xScale, p.yScale, plot, macdLine, signalLine);
refresh(g, p.accessor, p.xScale, p.yScale, plot, differenceGenerator, macdLine, signalLine);
};

function binder() {
differenceGenerator = plot.joinPath(p.accessor, p.xScale, p.yScale, differencePath);
macdLine.init(p.accessor.d, p.xScale, p.accessor.m, p.yScale);
signalLine.init(p.accessor.d, p.xScale, p.accessor.s, p.yScale);
}
Expand All @@ -1647,8 +1627,8 @@ module.exports = function(accessor_macd, plot, plotMixin) { // Injected depende
};
};

function refresh(g, accessor, x, y, plot, macdLine, signalLine) {
g.selectAll('path.difference').attr('d', differencePath(accessor, x, y));
function refresh(g, accessor, x, y, plot, differenceGenerator, macdLine, signalLine) {
g.selectAll('path.difference').attr('d', differenceGenerator);
g.selectAll('path.zero').attr('d', plot.horizontalPathLine(accessor.d, x, accessor.z, y));
g.selectAll('path.macd').attr('d', macdLine);
g.selectAll('path.signal').attr('d', signalLine);
Expand All @@ -1674,30 +1654,32 @@ function differencePath(accessor, x, y) {

module.exports = function(d3_scale_linear, d3_extent, accessor_ohlc, plot, plotMixin) { // Injected dependencies
return function() { // Closure constructor
var p = {}; // Container for private, direct access mixed in variables
var p = {}, // Container for private, direct access mixed in variables
ohlcGenerator;

function ohlc(g) {
plot.groupSelect(g, plot.dataMapper.unity, p.accessor.d)
.entry.append('path').attr({ class: 'ohlc' });
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d);

plot.appendUpDownEqual(group.selection, p.accessor, 'ohlc');

ohlc.refresh(g);
}

ohlc.refresh = function(g) {
refresh(g, plot, p.accessor, p.xScale, p.yScale);
g.selectAll('path.ohlc').attr('d', ohlcGenerator);
};

function binder() {
ohlcGenerator = plot.joinPath(p.accessor, p.xScale, p.yScale, ohlcPath);
}

// Mixin 'superclass' methods and variables
plotMixin(ohlc, p).plot(accessor_ohlc());
plotMixin(ohlc, p).plot(accessor_ohlc(), binder);

return ohlc;
};
};

function refresh(g, plot, accessor, x, y) {
g.selectAll('path.ohlc').attr({ d: ohlcPath(accessor, x, y) }).classed(plot.classedUpDown(accessor));
}

function ohlcPath(accessor, x, y) {
return function(d) {
var open = y(accessor.o(d)),
Expand Down Expand Up @@ -1750,6 +1732,38 @@ module.exports = function(d3_svg_line, d3_select) {
return line;
}

function up(accessor, d) {
return accessor.o(d) < accessor.c(d);
}

function down(accessor, d) {
return accessor.o(d) > accessor.c(d);
}

function groupUpDownEqual(data, accessor) {
return data.reduce(function(result, d) {
if (up(accessor, d)) result.up.push(d);
else if (down(accessor, d)) result.down.push(d);
else result.equal.push(d);
return result;
}, { up: [], down: [], equal: [] });
}

function appendUpDownEqual(g, accessor, plotName, upDownEqual) {
var plotNames = plotName instanceof Array ? plotName : [plotName];

upDownEqual = upDownEqual || groupUpDownEqual(g.datum(), accessor);

appendPlotType(g, upDownEqual.up, plotNames, 'up');
appendPlotType(g, upDownEqual.down, plotNames, 'down');
appendPlotType(g, upDownEqual.equal, plotNames, 'equal');
}

function appendPlotType(g, data, plotNames, direction) {
g.selectAll('path.' + plotNames.join('.') + '.' + direction).data([data])
.enter().append('path').attr('class', plotNames.join(' ') + ' ' + direction);
}

return {
dataMapper: {
unity: function(d) { return d; },
Expand All @@ -1769,12 +1783,9 @@ module.exports = function(d3_svg_line, d3_select) {
};
},

classedUpDown: function(accessor) {
return {
up: function(d) { return accessor.o(d) < accessor.c(d); },
down: function(d) { return accessor.o(d) > accessor.c(d); }
};
},
groupUpDownEqual: groupUpDownEqual,

appendUpDownEqual: appendUpDownEqual,

horizontalPathLine: function(accessor_date, x, accessor_value, y) {
return function(d) {
Expand All @@ -1790,6 +1801,12 @@ module.exports = function(d3_svg_line, d3_select) {

pathLine: PathLine,

joinPath: function(accessor, x, y, path) {
return function(data) {
return data.map(path(accessor, x, y)).join(' ');
};
},

interaction: {
mousedispatch: function(dispatch) {
return function(selection) {
Expand Down Expand Up @@ -2189,35 +2206,33 @@ function trendlineEnd(accessor_x, x, accessor_y, y) {

module.exports = function(accessor_volume, plot, plotMixin) { // Injected dependencies
return function() { // Closure function
var p = {}; // Container for private, direct access mixed in variables
var p = {}, // Container for private, direct access mixed in variables
volumeGenerator;

function volume(g) {
var group = plot.groupSelect(g, plot.dataMapper.unity, p.accessor.d)
.entry.append('path')
.attr('class', 'volume');
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d);

if(p.accessor.o && p.accessor.c) {
group.classed(plot.classedUpDown(p.accessor));
}
if(p.accessor.o && p.accessor.c) plot.appendUpDownEqual(group.selection, p.accessor, 'volume');
else group.entry.append('path').attr('class', 'volume');

volume.refresh(g);
}

volume.refresh = function(g) {
refresh(g, p.accessor, p.xScale, p.yScale);
g.selectAll('path.volume').attr('d', volumeGenerator);
};

function binder() {
volumeGenerator = plot.joinPath(p.accessor, p.xScale, p.yScale, volumePath);
}

// Mixin 'superclass' methods and variables
plotMixin(volume, p).plot(accessor_volume());
plotMixin(volume, p).plot(accessor_volume(), binder);

return volume;
};
};

function refresh(g, accessor, x, y) {
g.selectAll('path.volume').attr('d', volumePath(accessor, x, y));
}

function volumePath(accessor, x, y) {
return function(d) {
var vol = accessor.v(d);
Expand Down
6 changes: 3 additions & 3 deletions dist/techan.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/techan.min.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/indicator/atrtrailingstop
Submodule atrtrailingstop updated from adea25 to 5402c6
2 changes: 1 addition & 1 deletion examples/indicator/ichimoku
Submodule ichimoku updated from 1c7a16 to d3354d
2 changes: 1 addition & 1 deletion examples/plot/axisannotation
Submodule axisannotation updated from 7f8a24 to 332ced
2 changes: 1 addition & 1 deletion examples/plot/candlestick
Submodule candlestick updated from 8cf0a4 to 091463
2 changes: 1 addition & 1 deletion examples/plot/crosshair
Submodule crosshair updated from 55c094 to bb1677
2 changes: 1 addition & 1 deletion examples/plot/supstance
Submodule supstance updated from 97941b to d03d20
2 changes: 1 addition & 1 deletion examples/plot/trendline
Submodule trendline updated from 1009c6 to 0ecd4f
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "techanjs",
"version": "0.5.0-4",
"version": "0.5.0-5",
"repository": {
"type": "git",
"url": "https://github.com/andredumas/techan.js.git"
Expand Down
44 changes: 14 additions & 30 deletions src/plot/candlestick.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,37 @@
module.exports = function(d3_scale_linear, d3_extent, accessor_ohlc, plot, plotMixin) { // Injected dependencies
return function() { // Closure constructor
var p = {}, // Container for private, direct access mixed in variables
volumeOpacity = false;
bodyPathGenerator,
wickGenerator;

function candlestick(g) {
var group = plot.groupSelect(g, plot.dataMapper.unity, p.accessor.d);
var group = plot.groupSelect(g, plot.dataMapper.array, p.accessor.d),
upDownEqual = plot.groupUpDownEqual(g.datum(), p.accessor);

// Two path's as wick and body can be styled slightly differently (stroke and fills)
group.entry.append('path').attr('class', 'candle body');
group.entry.append('path').attr('class', 'candle wick');
// 3x2 path's as wick and body can be styled slightly differently (stroke and fills)
plot.appendUpDownEqual(group.selection, p.accessor, ['candle', 'body'], upDownEqual);
plot.appendUpDownEqual(group.selection, p.accessor, ['candle', 'wick'], upDownEqual);

candlestick.refresh(g);
}

candlestick.refresh = function(g) {
if(volumeOpacity) opacity(g, d3_scale_linear, d3_extent, p.accessor.v);
refresh(g, plot, p.accessor, p.xScale, p.yScale);
g.selectAll('path.candle.body').attr('d', bodyPathGenerator);
g.selectAll('path.candle.wick').attr('d', wickGenerator);
};

candlestick.volumeOpacity = function(_) {
if (!arguments.length) return volumeOpacity;
volumeOpacity = _;
return candlestick;
};
function binder() {
bodyPathGenerator = plot.joinPath(p.accessor, p.xScale, p.yScale, bodyPath);
wickGenerator = plot.joinPath(p.accessor, p.xScale, p.yScale, wickPath);
}

// Mixin 'superclass' methods and variables
plotMixin(candlestick, p).plot(accessor_ohlc());
plotMixin(candlestick, p).plot(accessor_ohlc(), binder);

return candlestick;
};
};

function refresh(g, plot, accessor, x, y) {
g.selectAll('path.candle.body').attr('d', bodyPath(accessor, x, y)).classed(plot.classedUpDown(accessor));
g.selectAll('path.candle.wick').attr('d', wickPath(accessor, x, y)).classed(plot.classedUpDown(accessor));
}

function bodyPath(accessor, x, y) {
return function(d) {
var path = [],
Expand Down Expand Up @@ -94,16 +90,4 @@ function wickPath(accessor, x, y) {

return path.join(' ');
};
}

function opacity(g, d3_scale_linear, d3_extent, accessor_volume) {
var selection = g.selectAll('g.data'),
volumeOpacityScale = d3_scale_linear()
.domain(d3_extent(selection.data().map(accessor_volume).filter(function(d) { return !isNaN(d); })))
.range([0.2, 1]);

selection.selectAll('path.candle').style('opacity', function(d) {
var volume = accessor_volume(d);
return isNaN(volume) ? null : volumeOpacityScale(volume);
});
}
Loading

0 comments on commit 467f67c

Please sign in to comment.