All Downloads are FREE. Search and download functionalities are using the official Maven repository.

js.ScalaMeter.chart.js Maven / Gradle / Ivy

The newest version!
var ScalaMeter = (function(parent) {
	var my = { name: "chart" };

	/*
	 * ----- imports -----
	 */
	var h,
		dKey,
		mapKey;

	/*
	 * ----- constants -----
	 */
	var MARGIN,
		WIDTH,
		HEIGHT,
		MIN_LEGEND_WIDTH,
		CHART_TYPES,
		Y_AXIS_CAPTION,
		ROOT_NODE;

	/*
	 * ----- private fields -----
	 */
	var
		svg_ = null,
		showCI_ = false,
		chartType_;

	/*
	 * ----- public functions -----
	 */	

	my.init = function() {
		h = parent.helper;
		dKey = h.dKey;
		mapKey = h.mapKey;

		MARGIN = {
			top : 20,
			right : 20,
			bottom : 30,
			left : 50
		};
		WIDTH = 900;
		HEIGHT = 500;
		MIN_LEGEND_WIDTH = 120;
		CHART_TYPES = {
			line: 0,
			bar: 1
		};
		Y_AXIS_CAPTION = "running time [ms]";
		ROOT_NODE = ".chart";

		chartType_ = CHART_TYPES.line;

		createSVG();
	};

	my.load = function(onLoad) {
		var storedData = parent.permalink.storedData();
		if (storedData != null) {
			setConfig(storedData.chartConfig);
		}
		onLoad();
	};

	my.getConfig = function() {
		return {
			type: chartType_,
			showCI: showCI_
		};
	};

	my.chartTypes = function() {
		return CHART_TYPES;
	};

	my.setType = function(_) {
		chartType_ = _;
		parent.filter.update();
	};

	my.toggleCI = function() {
		showCI_ = !showCI_;
		parent.filter.update();
	};

	my.update = function(data, scopeTree) {
		var filterDimensions = parent.dimensions;
		var keysCurveColor = h.unique(data, h.curveKey, d3.ascending);
		var legendWidth = MIN_LEGEND_WIDTH + 20 * keysCurveColor.length;
		var W = WIDTH - MARGIN.left - MARGIN.right - legendWidth;
		var H = HEIGHT - MARGIN.top - MARGIN.bottom;
		var allDimensions = filterDimensions.getAll();

		var keyAbscissa,
				showXGrid,
				lineData = [],
				groupedLineData = [],
				barData = [];

		// scales
		var x,
			y = d3.scale.linear()
				.domain([0, d3.max(data, mapKey(dKey.value))])
				.range([H, 0]);

		var legendDimensions = allDimensions.slice(1);

		var legendTitle = legendDimensions.map(function(dim) {
			return dim.caption();
		}).join(" \u2192 ");

		var legendSize = 1;

		var xAxisDim = allDimensions[0];

		var barScale = function() { return 0; };

		switch(chartType_) {
			case CHART_TYPES.line:
				filterDimensions.filterValues(data, d3.descending);
				lineData = data;
				showXGrid = true;
				keyAbscissa = xAxisDim.keyFn();

				if (xAxisDim.key() == dKey.date) {
					x = d3.scale.ordinal()
								.domain(xAxisDim.filteredValues())
								.rangePoints([0, W]);
				} else {
					x = d3.scale.linear()
								.domain(d3.extent(data, keyAbscissa))
								.range([0, W]);
				}

				var nestLineData = d3.nest().key(h.curveKey);
				legendDimensions.forEach(function(dim) {
					nestLineData.key(dim.keyFn());
				});
				nestLineData.sortValues(h.sortBy(keyAbscissa));

				groupedLineData = nestLineData.entries(lineData);
				break;
			case CHART_TYPES.bar:
				filterDimensions.filterValues(data, d3.ascending);
				barData = data;
				showXGrid = false;

				function extendBarScale(parent, xi, keyFn) {
					return function(d) {
						return parent(d) + xi(keyFn(d));
					};
				}

				var parentWidth = W;
				var xN = allDimensions.map(function(dim) {
					var xi = d3.scale.ordinal()
						.domain(dim.filteredValues())
						.rangeRoundBands([0, parentWidth], 0.1);
					parentWidth = xi.rangeBand();
					barScale = extendBarScale(barScale, xi, dim.keyFn());
					return xi;
				});
				var xi = d3.scale.ordinal()
					.domain(keysCurveColor)
					.rangeRoundBands([0, parentWidth], 0);
				barScale = extendBarScale(barScale, xi, h.curveKey);

				var barWidth = xi.rangeBand();
				x = xN[0];

				break;
		}

		legendDimensions.forEach(function(dim) {
			legendSize *= dim.filteredValues().length;
		});

		var brightnessScale = d3.scale.linear()
			.domain([0, legendSize - 1])
			.range([-1, 1]);

        if (data.length > 0) {
            Y_AXIS_CAPTION = data[0].units;
        }
		d3.transition().each(function() {
			// axis and grid
			if (showXGrid) {
				var xGrid = d3.svg.axis().scale(x).orient("bottom").tickSize(-H, 0, 0).tickFormat("");
				svg_.select(".x.grid")
					.attr("transform", "translate(0," + H + ")")
					.transition().call(xGrid);
			} else {
				svg_.select(".x.grid").selectAll("*").remove();
			}

			var yGrid = d3.svg.axis().scale(y).orient("left").tickSize(-W, 0, 0).tickFormat("");
			svg_.select(".y.grid").transition().call(yGrid);

			var xAxis = d3.svg.axis().scale(x).orient("bottom").tickFormat(xAxisDim.format());
			svg_.select(".x.axis")
				.attr("transform", "translate(0," + H + ")")
				.transition()
				.call(xAxis)
				.select(".x-axis-caption")
				.attr("x", W)
				.text(xAxisDim.caption());

			var yAxis = d3.svg.axis().scale(y).orient("left");
			console.log(Y_AXIS_CAPTION);
			svg_.select(".y.axis")
			    .transition()
			    .call(yAxis)
			    .select(".y-axis-caption")
			    .text(Y_AXIS_CAPTION);

			var bars = svg_.selectAll("rect").data(barData, mapKey(dKey.index));

			bars.enter()
				.append("rect")
				.style("fill-opacity", 0);

			bars
				.each(hoverable("bar", legendKey))
				.each(bar);

			bars.exit().remove();

			var barsCI = svg_.selectAll(".bar-ci").data(showCI_ ? barData : [], mapKey(dKey.index));

			barsCI.enter()
				.append("g")
				.attr("class", "bar-ci")
				.each(createBarCI);

			barsCI.each(barCI);

			barsCI.exit().remove();

			// data points
			var points = svg_.selectAll('circle').data(lineData, mapKey(dKey.index));

			points.enter().append('circle')
				.attr('r', 5);

			points
				.each(hoverable("line", legendKey))
				.style("stroke",  function(d) { return colorMap(d); })
				.transition()
				.attr('cx', function (d) { return x(keyAbscissa(d)); })
				.attr('cy', function (d) { return y(d[dKey.value]); });

			points.exit().remove();

			// data paths
			var curves = svg_.selectAll(".curve").data(groupedLineData, h.fKey);

			curves.enter()
				.append("g")
				.attr("class", "curve");
			
			curves.each(curveFn(path, 1));

			curves.exit().remove();

			// confidence intervals (areas)
			var curvesCI = svg_.selectAll(".curve-ci").data(showCI_ ? groupedLineData : [], h.fKey);

			curvesCI.enter()
				.insert("g", ":first-child")
				.attr("class", "curve-ci");
			
			curvesCI.each(curveFn(areaCI, 1));

			curvesCI.exit().remove();

			svg_.select(".legend-container")
				.attr("transform", "translate(" + (W + legendWidth - 20 * keysCurveColor.length) + ", 0)")
				.each(legendGroup(0, 0, legendSize, ""))
				.select(".legend-title")
				.attr("x", -4)
				.attr("y", 13)
				.text(legendTitle);
		});

		function curveColorWithShade(curveKey, shade) {
			return d3.hsl(h.mainColors(curveKey)).brighter(brightnessScale(shade));
		}

		function colorMap(d) {
			var index = 0;
			legendDimensions.forEach(function(dim) {
				index = index * dim.filteredValues().length + dim.filteredValues().indexOf(dim.keyFn()(d));
			});
			return curveColorWithShade(h.curveKey(d), index);
		}

		function path(d) {
			var line = d3.svg.line().x(function(d) {
				return x(keyAbscissa(d));
			}).y(function(d) {
				return y(d[dKey.value]);
			});

			var d0 = d.values[0];
			var g = d3.select(this);
			var color = colorMap(d0);
			var pathData;
			if (keyAbscissa(d0) == null) {
				pathData = "M0," + y(d0[dKey.value]) + "H" + W;
			} else {
				pathData = line(d.values);
			}
			g.select("path")
				.attr("style", "stroke-opacity:0.7;stroke:" + color)
				.each(hoverable("line", legendKey(d0)))
				.transition()
				.attr("d", pathData);
		}

		function areaCI(d) {
			var area = d3.svg.area().x(function(d) {
				return x(keyAbscissa(d));
			}).y0(function(d) {
				return y(d[dKey.cilo]);
			}).y1(function(d) {
				return y(d[dKey.cihi]);
			});

			var g = d3.select(this);
			var color = colorMap(d.values[0]);
			g.select("path")
				.attr("class", "area-" + legendKey(d.values[0]))
				.attr("style", "stroke-opacity:0.2;stroke:" + color + ";fill-opacity:0.1;fill:" + color)
				.transition()
				.attr("d", area(d.values));
		}

		function curveFn(fn, depth) {
			return function(d) {
				var groups = d3.select(this).selectAll(".group-" + depth).data(d.values, h.fKey);

				var subGroups = groups.enter()
					.append("g")
					.attr("class", "group-" + depth);

				if (depth < legendDimensions.length) {
					groups.each(curveFn(fn, depth + 1));
				} else {
					subGroups
						.append("path");

					groups.each(fn);
				}
				groups.exit().remove();
			}
		}

		function bar(d) {
			d3.select(this)
				.style("fill", colorMap(d))
				.transition()
				.attr("width", barWidth)
				.attr("x", barScale(d))
				.attr("y", y(d[dKey.value]))
				.attr("height", H - y(d[dKey.value]))
				.transition()
				.style("fill-opacity", 1);

			var ttContent = legendDimensions.map(function(dim) {
				return dim.caption() + ": " + dim.format()(dim.keyFn()(d));
			});
			ttContent.push("success: " + d[dKey.success]);

			$(this).tooltip({title: ttContent.join("
"), container: "body", html: true}); } function createBarCI() { var g = d3.select(this); g.append("line") .attr("class", "bar-cilo bar-ci-v bar-cilo-v").style("stroke-opacity", 0); g.append("line") .attr("class", "bar-cihi bar-ci-v bar-cihi-v").style("stroke-opacity", 0); g.append("line") .attr("class", "bar-cilo bar-ci-h bar-cilo-h").style("stroke-opacity", 0); g.append("line") .attr("class", "bar-cihi bar-ci-h bar-cihi-h").style("stroke-opacity", 0); } function barCI(d) { var g = d3.select(this) var x0 = barScale(d); var xMiddle = x0 + barWidth / 2; var y0 = y(d[dKey.value]); var yCilo = y(d[dKey.cilo]); var yCihi = y(d[dKey.cihi]); lineTransition(".bar-ci-h", function(nodes) { return nodes .attr("x1", x0 + 1) .attr("x2", x0 + barWidth - 1); }); lineTransition(".bar-cilo-h", function(nodes) { return nodes .attr("y1", yCilo) .attr("y2", yCilo); }); lineTransition(".bar-cihi-h", function(nodes) { return nodes .attr("y1", yCihi) .attr("y2", yCihi); }); lineTransition(".bar-ci-v", function(nodes) { return nodes .attr("x1", xMiddle) .attr("x2", xMiddle); }); lineTransition(".bar-cilo-v", function(nodes) { return nodes .attr("y1", y0) .attr("y2", yCilo); }); lineTransition(".bar-cihi-v", function(nodes) { return nodes .attr("y1", y0) .attr("y2", yCihi); }); function lineTransition(selector, fn) { g.selectAll(selector) .transition() .call(fn) .transition() .style("stroke-opacity", 1); } } function legendRow(shade) { return function(d, i) { var g = d3.select(this); var rects = g.selectAll("rect").data(keysCurveColor, h.ident); rects.enter().append("rect") .attr("width", 18) .attr("height", 18) .style("fill", function(curveKey) { return curveColorWithShade(curveKey, shade + i) } ); rects.attr("x", function(d, i) { return 20 * i; } ); rects.exit().remove(); }; } function legendGroup(depth, parentId, groupSize, lKeyPrefix) { return function(d, i) { var id = parentId + i * groupSize; var dim = legendDimensions[depth]; var groups = d3.select(this).selectAll(".legend-grp-" + depth).data(dim.filteredValues(), h.ident); var subGroupSize = groupSize / dim.filteredValues().length; var lKey = (depth == 0) ? "" : lKeyPrefix + d + "-"; var labels = groups.enter() .append("g") .attr("class", "legend-grp-" + depth) .append("text") .attr("x", -4) .attr("y", 13) .text(dim.format()); if (depth < legendDimensions.length - 1) { labels .attr("style", "font-weight:bold"); groups .attr("transform", function(d, i) { return "translate(0, " + ((1 + i * (subGroupSize + 1)) * 20) + ")"; }) .each(legendGroup(depth + 1, id, subGroupSize, lKey)); } else { groups .attr("transform", function(d, i) { return "translate(0, " + ((1 + i) * 20) + ")"; }) .each(hoverable("legend", function(d) { return lKey + d; })) .each(legendRow(id)); } groups.exit().remove(); }; } function legendKey(d) { return legendDimensions.map(function(dim) { var key = dim.keyFn()(d); return key == null ? "null" : key; }).join("-"); } } /* * ----- private functions ----- */ function setConfig(config) { chartType_ = config.type; showCI_ = config.showCI; d3.selectAll(".nav-charttype li") .classed("active", function(d, i) { return i == chartType_; }); if (showCI_) { $('.btn-showCI').button('toggle'); } } function createSVG() { svg_ = d3.select(ROOT_NODE) .append("svg") .attr("width", WIDTH) .attr("height", HEIGHT) .append("g") .attr("transform", "translate(" + MARGIN.left + "," + MARGIN.top + ")"); // grid svg_.append("g") .attr("class", "x grid"); svg_.append("g") .attr("class", "y grid"); // x axis svg_.append("g") .attr("class", "x axis") .append("text") .attr("class", "x-axis-caption") .attr("y", -6) .style("text-anchor", "end"); // y axis svg_.append("g") .attr("class", "y axis") .append("text") .attr("class", "y-axis-caption") .attr("transform", "rotate(-90)") .attr("y", 6).attr("dy", ".71em") .style("text-anchor", "end") .text(Y_AXIS_CAPTION); // legend svg_.append("g") .attr("class", "legend-container") .append("text") .attr("class", "legend-title"); } function hoverable(className, getId) { return function(d) { var id = ($.isFunction(getId)) ? getId(d) : getId; d3.select(this) .classed(className, true) .classed(className + "-" + id, true) .on("mouseover", function() { mover(id) }) .on("mouseout", function() { mout(id) }); }; } function mover(id) { var line = d3.selectAll(".line-" + id); var area = d3.select(".area-" + id); var legend = d3.select(".legend-" + id) var bars = d3.selectAll(".bar-" + id); line.transition().style("stroke-width", 4); area.transition() .style("stroke-opacity", 1) .style("fill-opacity", 0.3); legend.select("text").attr("style", "font-weight:bold"); legend.selectAll("rect") .transition() .style("stroke-opacity", 1); bars.transition().style("stroke-opacity", 1); } function mout(id) { var line = d3.selectAll(".line-" + id); var area = d3.select(".area-" + id); var legend = d3.select(".legend-" + id) var bars = d3.selectAll(".bar-" + id); line.transition().style("stroke-width", 1.5); area.transition() .style("stroke-opacity", 0.2) .style("fill-opacity", 0.1); legend.select("text").attr("style", "font-weight:normal"); legend.selectAll("rect") .transition() .style("stroke-opacity", 0); bars.transition().style("stroke-opacity", 0); } parent[my.name] = my; return parent; })(ScalaMeter || {});




© 2015 - 2025 Weber Informatics LLC | Privacy Policy