Comments (4)
The area-based calculations are correct for standard funnel views (it's questionable for pyramid views, see #25). Despite this, it does lead to different heights, because subsequent blocks have smaller widths to work with. See the comparisons below for equally weighted blocks that have different bottom widths:
http://jsfiddle.net/3ctsrs60/1/ (equal heights due to non-changing width)
http://jsfiddle.net/okLpk1h9/1/ (different heights due to changing widths)
I agree that this behavior should probably change, because it leads to unintuitive heights and causes its own set of problems with an inverted view. The math calculation is a bit easier when slicing blocks based on proportional areas, but it's not worth the trouble or unintuitive nature. Making height-based calculations would effectively fix #25.
This changes break backwards compatibility, but I was planning on making the v0.7 release break backwards compatibility anyway to standardize some options. Therefore, I think this is appropriate to target this for the v0.7 release.
I am curious to see what modifications you made to the plugin.
from d3-funnel.
For my project, this does the job
key: '_makePaths',
value: function _makePaths() {
var paths = [];
// Initialize velocity
var dx = this.dx;
var dy = this.dy;
// Initialize starting positions
var prevLeftX = 0;
var prevRightX = this.width;
var prevHeight = 0;
// Start from the bottom for inverted
if (this.isInverted) {
prevLeftX = this.bottomLeftX;
prevRightX = this.width - this.bottomLeftX;
}
// Initialize next positions
var nextLeftX = 0;
var nextRightX = 0;
var nextHeight = 0;
var middle = this.width / 2;
// Move down if there is an initial curve
if (this.isCurved) {
prevHeight = 10;
}
var topBase = this.width;
var bottomBase = 0;
var totalArea = this.height * (this.width + this.bottomWidth) / 2;
var slope = 2 * this.height / (this.width - this.bottomWidth);
// This is greedy in that the block will have a guaranteed height
// and the remaining is shared among the ratio, instead of being
// shared according to the remaining minus the guaranteed
if (this.minHeight !== false) {
var height = this.height - this.minHeight * this.data.length;
totalArea = height * (this.width + this.bottomWidth) / 2;
}
var totalCount = 0;
var count = 0;
// Harvest total count
var sum = 0;
for (var i = 0; i < this.data.length; i++) {
totalCount += isArray(this.data[i][1]) ? this.data[i][1][0] : this.data[i][1];
sum += this.data[i][1][0]; //starting edits
}
// Create the path definition for each funnel block
// Remember to loop back to the beginning point for a closed path
//starting edits
var exceptionValueCount = 0;
for (var i = 0; i < this.data.length; i++) {
if (this.data[i][1][0] === 0 || Math.round((this.data[i][1][0] * this.height) / sum) <= this.minHeight)
exceptionValueCount++;
}
sum = (Math.round(sum * 100) / 100);
//ending edits
for (var i = 0; i < this.data.length; i++) {
count = isArray(this.data[i][1]) ? this.data[i][1][0] : this.data[i][1];
// Calculate dynamic shapes based on area
if (this.dynamicArea) {
var ratio = count / totalCount;
var area = ratio * totalArea;
if (this.minHeight !== false) {
area += this.minHeight * (this.width + this.bottomWidth) / 2;
}
bottomBase = Math.sqrt((slope * topBase * topBase - 4 * area) / slope);
dx = topBase / 2 - bottomBase / 2;
//starting edits
//dy = area * 2 / (topBase + bottomBase);
var y = (sum === 0 || ((this.data[i][1][0] * this.height) / sum) < this.minHeight) ? this.minHeight : (this.data[i][1][0] * (this.height - (exceptionValueCount * this.minHeight))) / sum;
dy = y;
//ending edits
if (this.isCurved) {
dy = dy - this.curveHeight / this.data.length;
}
topBase = bottomBase;
}
// Stop velocity for pinched blocks
if (this.bottomPinch > 0) {
// Check if we've reached the bottom of the pinch
// If so, stop changing on x
if (!this.isInverted) {
if (i >= this.data.length - this.bottomPinch) {
dx = 0;
}
// Pinch at the first blocks relating to the bottom pinch
// Revert back to normal velocity after pinch
} else {
// Revert velocity back to the initial if we are using
// static area's (prevents zero velocity if isInverted
// and bottomPinch are non trivial and dynamicArea is
// false)
if (!this.dynamicArea) {
dx = this.dx;
}
dx = i < this.bottomPinch ? 0 : dx;
}
}
// Calculate the position of next block
nextLeftX = prevLeftX + dx;
nextRightX = prevRightX - dx;
nextHeight = prevHeight + dy;
// Expand outward if inverted
if (this.isInverted) {
nextLeftX = prevLeftX - dx;
nextRightX = prevRightX + dx;
}
// Plot curved lines
if (this.isCurved) {
paths.push([
// Top Bezier curve
[prevLeftX, prevHeight, 'M'], [middle, prevHeight + (this.curveHeight - 10), 'Q'], [prevRightX, prevHeight, ''],
// Right line
[nextRightX, nextHeight, 'L'],
// Bottom Bezier curve
[nextRightX, nextHeight, 'M'], [middle, nextHeight + this.curveHeight, 'Q'], [nextLeftX, nextHeight, ''],
// Left line
[prevLeftX, prevHeight, 'L']]);
// Plot straight lines
} else {
paths.push([
// Start position
[prevLeftX, prevHeight, 'M'],
// Move to right
[prevRightX, prevHeight, 'L'],
// Move down
[nextRightX, nextHeight, 'L'],
// Move to left
[nextLeftX, nextHeight, 'L'],
// Wrap back to top
[prevLeftX, prevHeight, 'L']]);
}
// Set the next block's previous position
prevLeftX = nextLeftX;
prevRightX = nextRightX;
prevHeight = nextHeight;
}
return paths;
}
from d3-funnel.
Significant headway has been made. The dynamic-height
branch includes changes for calculating heights directly rather than through trapezoid area.
To-do:
- Fix broken interactions with
isInverted
- Rename
dynamicArea
option to a more sensible name
from d3-funnel.
Resolved with v0.7.0 release.
from d3-funnel.
Related Issues (20)
- An in-range update of webpack is breaking the build 🚨 HOT 3
- Can the top layer be a triangle? HOT 2
- An in-range update of d3-scale is breaking the build 🚨 HOT 2
- Elements
- FunnelGraph call fail HOT 2
- Cannot read property 'style' of null HOT 1
- Power BI Custom Visual HOT 1
- Funnel chart with different neck width
- Access click callback in angular HOT 2
- Tooltip position incorrect in latest versions of Chrome HOT 3
- Get formatted label inside format function HOT 1
- Opacity in fill of d3-funnel HOT 2
- please Add options whenever mouseover it should zoom each label. HOT 3
- Typescript declaration
- TypeScript type is any HOT 5
- TypeError: d3_funnel__WEBPACK_IMPORTED_MODULE_0__.D3Funnel is not a constructor HOT 1
- Zero value and dynamicSlope HOT 1
- Funnel - Stacked
- Tooltip is not working for all charts HOT 1
- Overflow text truncates. Can we use css ellipsis formatting HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from d3-funnel.