Skip to content

Commit

Permalink
feat(plugin): Intent to ship TextOverlap
Browse files Browse the repository at this point in the history
- Split data.labels.overlap into TextOverlap plugin
- Update head license comments & describe name for stanford diagram

Fix #1048
  • Loading branch information
netil authored Aug 23, 2019
1 parent 05de6cf commit 728e879
Show file tree
Hide file tree
Showing 13 changed files with 328 additions and 172 deletions.
47 changes: 27 additions & 20 deletions demo/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -1886,26 +1886,6 @@ var demos = {
}
}
},
DataLabelOverlap: {
options: {
data: {
columns: [
["data1", 3, 3.8, 2, 3.8, 15, 2.5, 5.4, 6, 5.5, 2.4],
["data2", 1.7, 1.0, 10, 20, 8.4, 5, 2.5, 12.3, 8.5, 9.8],
["data3", 2.3, 15.3, 8.5, 30, 25, 12.0, 5, 8.4, 9.9, 28],
["data4", 30, 35, 20, 38, 19, 28, 5.6, 8, 5.5, 22],
["data5", 13, 10, 10, 20, 8, 5, 20, 13, 18.5, 9.8]
],
labels:{
overlap: {
extent: 8,
area : 3
}
}
}
},
description: "Prevents data label texts overlap using <a href='https://en.wikipedia.org/wiki/Voronoi_diagram' target='_new'>Voronoi layout</a>."
},
DataLabelPosition: {
options: {
data: {
Expand Down Expand Up @@ -2453,6 +2433,33 @@ d3.select(".chart_area")
"#stanfordDiagram .test-polygon5 { fill: orange; }",
"#stanfordDiagram .test-polygon5 text { fill: black; }"
]
},
TextOverlap: {
description: "Prevents data label texts overlap using <a href='https://en.wikipedia.org/wiki/Voronoi_diagram' target='_new'>Voronoi layout</a>.<br>Must load or import plugin before the use.",
options: {
data: {
columns: [
["data1", 3, 3.8, 2, 3.8, 15, 2.5, 5.4, 6, 5.5, 2.4],
["data2", 1.7, 1.0, 10, 20, 8.4, 5, 2.5, 12.3, 8.5, 9.8],
["data3", 2.3, 15.3, 8.5, 30, 25, 12.0, 5, 8.4, 9.9, 28],
["data4", 30, 35, 20, 38, 19, 28, 5.6, 8, 5.5, 22],
["data5", 13, 10, 10, 20, 8, 5, 20, 13, 18.5, 9.8]
],
labels:{
overlap: {
extent: 8,
area : 3
}
}
},
_plugins: [{
textoverlap: {
selector: ".bb-texts text",
extent: 8,
area : 3
}
}]
}
}
},

Expand Down
3 changes: 3 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ <h4 class="sub_tit">Sample code</h4>
fallback.load({
plugins_stanford: path.map(function(p) {
return p + "plugin/billboardjs-plugin-stanford.js"
}),
plugins_textoverlap: path.map(function(p) {
return p + "plugin/billboardjs-plugin-textoverlap.js"
})
});

Expand Down
65 changes: 0 additions & 65 deletions spec/internals/data-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -557,71 +557,6 @@ describe("DATA", () => {
});
});

it("set options data.labels.overlap", () => {
args.data.labels = {
overlap: false
};
});

it("should move data labels into correct position", () => {
const expectedTextDy = {
data1: ["0.35em", "0.71em", "0.71em"],
data2: ["0.35em", "0.35em", "0.71em"],
data3: ["0.71em", "0.71em", "0.35em"],
data4: ["0.71em", "0.35em", "0.35em"]
};
const expectedTextTransform = {
data1: ["translate(-1, -1)", "translate(-1, 6)", "translate(-1, 6)"],
data2: ["translate(-1, -1)", "translate(-1, -1)", "translate(-1, 6)"],
data3: ["translate(-1, 6)", "translate(-1, 6)", "translate(-1, -1)"],
data4: ["translate(-1, 6)", "translate(-1, -1)", "translate(-1, -1)"]
};

Object.keys(expectedTextDy).forEach(key => {
chart.internal.main.selectAll(`.${CLASS.texts}-${key} text.${CLASS.text}`).each(function(d, i) {
const text = d3.select(this);

expect(text.attr("dy")).to.be.equal(expectedTextDy[key][i]);
expect(text.attr("transform")).to.be.equal(expectedTextTransform[key][i]);
});
});

});

it("set options data.labels.overlap", () => {
args.data.labels = {
overlap: {
extent: 8,
area: 3
}
};
});

it("should move data labels into correct position with specified extent and area", () => {
const expectedTextDy = {
data1: ["0.35em", "0.71em", "0.71em"],
data2: ["0.35em", "0.35em", "0.71em"],
data3: ["0.71em", "0.71em", "0.35em"],
data4: ["0.71em", "0.35em", "0.35em"]
};
const expectedTextTransform = {
data1: ["translate(-8, -8)", "translate(-8, 13)", "translate(-8, 13)"],
data2: ["translate(-8, -8)", "translate(-8, -8)", "translate(-8, 13)"],
data3: ["translate(-8, 13)", "translate(-8, 13)", "translate(-8, -8)"],
data4: ["translate(-8, 13)", "translate(-8, -8)", "translate(-8, -8)"]
};

Object.keys(expectedTextDy).forEach(key => {
chart.internal.main.selectAll(`.${CLASS.texts}-${key} text.${CLASS.text}`).each(function(d, i) {
const text = d3.select(this);

expect(text.attr("dy")).to.be.equal(expectedTextDy[key][i]);
expect(text.attr("transform")).to.be.equal(expectedTextTransform[key][i]);
});
});

});

it("set options data.labels.position", () => {
args.data.labels = {
position: {
Expand Down
6 changes: 5 additions & 1 deletion spec/plugin/stanford/stanford-elements-spec.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
/**
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*/
/* eslint-disable */
import util from "../../assets/util";
import Stanford from "../../../src/plugin/stanford/index";
import CLASS from "../../../src/plugin/stanford/classes";

describe("STANFORD ELEMENTS", () => {
describe("PLUGIN: STANFORD ELEMENTS", () => {
let chart;
const expectedLines = [
{x1: 0, y1: 0, x2: 65, y2: 65, class: "line1"},
Expand Down
6 changes: 5 additions & 1 deletion spec/plugin/stanford/stanford-spec.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
/**
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*/
/* eslint-disable */
import util from "../../assets/util";
import Stanford from "../../../src/plugin/stanford/index";
import {compareEpochs, getCentroid, getRegionArea, pointInRegion} from "../../../src/plugin/stanford/util";

describe("STANFORD", () => {
describe("PLUGIN: STANFORD", () => {
let chart;
let stanford = new Stanford({ epochs: [30, 35] });
let args = {
Expand Down
95 changes: 95 additions & 0 deletions spec/plugin/textoverlap/textoverlap-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*/
/* eslint-disable */
import util from "../../assets/util";
import CLASS from "../../../src/config/classes";
import TextOverlap from "../../../src/plugin/textoverlap";

describe("PLUGIN: TEXTOVERLAP", () => {
let chart;
let args = {
padding: {
left: 50
},
data: {
columns: [
["data1", 1030, 2200, 2100],
["data2", 1150, 2010, 1200],
["data3", -1150, -2010, -1200],
["data4", -1030, -2200, -2100]
],
type: "line",
labels: true,
groups: [
["data1", "data2"],
["data3", "data4"]
]
},
plugins: [
new TextOverlap()
]
};

beforeEach(() => {
chart = util.generate(args);
});

it("should move data labels into correct position", () => {
const expectedTextDy = {
data1: ["0.35em", "0.71em", "0.71em"],
data2: ["0.35em", "0.35em", "0.71em"],
data3: ["0.71em", "0.71em", "0.35em"],
data4: ["0.71em", "0.35em", "0.35em"]
};
const expectedTextTransform = {
data1: ["translate(-1, -1)", "translate(-1, 6)", "translate(-1, 6)"],
data2: ["translate(-1, -1)", "translate(-1, -1)", "translate(-1, 6)"],
data3: ["translate(-1, 6)", "translate(-1, 6)", "translate(-1, -1)"],
data4: ["translate(-1, 6)", "translate(-1, -1)", "translate(-1, -1)"]
};

Object.keys(expectedTextDy).forEach(key => {
chart.internal.main.selectAll(`.${CLASS.texts}-${key} text.${CLASS.text}`).each(function(d, i) {
const text = d3.select(this);

expect(text.attr("dy")).to.be.equal(expectedTextDy[key][i]);
expect(text.attr("transform")).to.be.equal(expectedTextTransform[key][i]);
});
});
});

it("set options extent & area options", () => {
args.plugins = [
new TextOverlap({
extent: 8,
area: 3
})
];
});

it("should move data labels into correct position with specified extent and area", () => {
const expectedTextDy = {
data1: ["0.35em", "0.71em", "0.71em"],
data2: ["0.35em", "0.35em", "0.71em"],
data3: ["0.71em", "0.71em", "0.35em"],
data4: ["0.71em", "0.35em", "0.35em"]
};
const expectedTextTransform = {
data1: ["translate(-8, -8)", "translate(-8, 13)", "translate(-8, 13)"],
data2: ["translate(-8, -8)", "translate(-8, -8)", "translate(-8, 13)"],
data3: ["translate(-8, 13)", "translate(-8, 13)", "translate(-8, -8)"],
data4: ["translate(-8, 13)", "translate(-8, -8)", "translate(-8, -8)"]
};

Object.keys(expectedTextDy).forEach(key => {
chart.internal.main.selectAll(`.${CLASS.texts}-${key} text.${CLASS.text}`).each(function(d, i) {
const text = d3.select(this);

expect(text.attr("dy")).to.be.equal(expectedTextDy[key][i]);
expect(text.attr("transform")).to.be.equal(expectedTextTransform[key][i]);
});
});
});
});
10 changes: 0 additions & 10 deletions src/config/Options.js
Original file line number Diff line number Diff line change
Expand Up @@ -615,15 +615,6 @@ export default class Options {
* data3: "green"
* },
*
* //turn on overlap prevention
* overlap: false,
*
* //set extent of prevent overlap, and minimum area needed to show a data label
* overlap: {
* extent : 6,
* area: 2,
* },
*
* // set x, y coordinate position
* position: {
* x: -10,
Expand All @@ -635,7 +626,6 @@ export default class Options {
data_labels: {},
data_labels_colors: undefined,
data_labels_position: {},
data_labels_overlap: true,

/**
* This option changes the order of stacking data and pieces of pie/donut.
Expand Down
60 changes: 0 additions & 60 deletions src/internals/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ import {
select as d3Select,
selectAll as d3SelectAll
} from "d3-selection";
import {voronoi as d3Voronoi} from "d3-voronoi";
import {
polygonCentroid as d3PolygonCentroid,
polygonArea as d3PolygonArea
} from "d3-polygon";
import ChartInternal from "./ChartInternal";
import CLASS from "../config/classes";
import {capitalize, extend, getBoundingRect, getRandom, isNumber, isObject, isString} from "./util";
Expand Down Expand Up @@ -100,40 +95,6 @@ extend(ChartInternal.prototype, {
return color || $$.color(d);
},

/**
* Set text label's position to preventg overlap.
* @param {Object} overlap Overlap config object
* @private
*/
preventLabelOverlap(overlap) {
const $$ = this;
const {extent = 1, area = 0} = overlap;
const cells = $$.generateVoronoi($$.mainText.data().map(v => [v.x, v.value]));
let i = 0;

$$.mainText.each(function() {
const cell = cells[i++];

if (cell && this) {
const [x, y] = cell.data;
const [cx, cy] = d3PolygonCentroid(cell);
const angle = Math.round(Math.atan2(cy - y, cx - x) / Math.PI * 2);

const xTranslate = extent * (angle === 0 ? 1 : -1);
const yTranslate = angle === -1 ? -extent : extent + 5;

const txtAnchor = Math.abs(angle) === 1 ?
"middle" : (angle === 0 ? "start" : "end");

d3Select(this)
.attr("display", d3PolygonArea(cell) < area ? "none" : null)
.attr("text-anchor", txtAnchor)
.attr("dy", `0.${angle === 1 ? 71 : 35}em`)
.attr("transform", `translate(${xTranslate}, ${yTranslate})`);
}
});
},

/**
* Redraw chartText
* @param {Function} x Positioning function for x
Expand All @@ -146,11 +107,6 @@ extend(ChartInternal.prototype, {
const $$ = this;
const t = getRandom();
const opacityForText = forFlow ? 0 : $$.opacityForText.bind($$);
const overlap = $$.config.data_labels_overlap;

if (overlap === false || isObject(overlap)) {
$$.preventLabelOverlap(overlap);
}

return [
this.mainText.each(function() {
Expand Down Expand Up @@ -230,22 +186,6 @@ extend(ChartInternal.prototype, {
};
},

/**
* Generates the voronoi layout for data labels
* @param {Object} data Indices values
* @returns {Object} Voronoi layout points and corresponding Data points
* @private
*/
generateVoronoi(data) {
const $$ = this;
const min = ["x", "y"].map(v => $$[v].domain()[0]);
const max = ["x", "y"].map(v => $$[v].domain()[1]);

return d3Voronoi()
.extent([min, max])
.polygons(data);
},

/**
* Get centerized text position for bar type data.label.text
* @private
Expand Down
Loading

0 comments on commit 728e879

Please sign in to comment.