Skip to content

Commit 8f1b56f

Browse files
Julien Castelainnetil
authored andcommitted
feat(point): Implement alternate markers
Implementation to allow use a customized data points Fix #209 Close #219
1 parent 9699a55 commit 8f1b56f

File tree

10 files changed

+251
-207
lines changed

10 files changed

+251
-207
lines changed

demo/demo.js

Lines changed: 43 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,79 +1606,59 @@ var demos = {
16061606
]
16071607
},
16081608
point: {
1609-
type: {
1610-
create: function(element, cssClassFn, sizeFn, fillStyleFn) {
1611-
return element.enter().append("polygon")
1612-
.attr("class", cssClassFn)
1613-
.style("fill", fillStyleFn);
1614-
},
1615-
update: function(element, xPosFn, yPosFn, opacityStyleFn, fillStyleFn,
1616-
withTransition, flow, selectedCircles) {
1617-
var size = this.pointR(element) * 3.0;
1618-
var halfSize = size * 0.5;
1619-
1620-
function getPoints(d) {
1621-
var x1 = xPosFn(d);
1622-
var y1 = yPosFn(d) - halfSize;
1623-
var x2 = x1 - halfSize;
1624-
var y2 = y1 + size;
1625-
var x3 = x1 + halfSize;
1626-
var y3 = y2;
1627-
1628-
return [x1, y1, x2, y2, x3, y3].join(" ");
1629-
}
1630-
1631-
return element
1632-
.attr("points", getPoints)
1633-
.style("opacity", opacityStyleFn)
1634-
.style("fill", fillStyleFn);
1635-
}
1636-
}
1609+
pattern: [
1610+
"<polygon points='2.5 0 0 5 5 5'></polygon>"
1611+
]
16371612
}
16381613
}
16391614
},
1640-
CustomPointsDiamond: {
1615+
CustomPointsDiamonds: {
16411616
options: {
16421617
data: {
16431618
columns: [
1644-
['data1', 100, 200, 1000, 900, 500],
1619+
['data1', 100, 400, 1000, 900, 500],
16451620
['data2', 20, 40, 500, 300, 200]
16461621
]
16471622
},
16481623
point: {
1649-
type: {
1650-
create: function(element, cssClassFn, sizeFn, fillStyle) {
1651-
// create custom an element node
1652-
return element.enter().append("polygon")
1653-
.attr("class", cssClassFn)
1654-
.style("fill", fillStyle);
1655-
},
1656-
1657-
update: function(element, xPosFn, yPosFn, opacityStyleFn, fillStyleFn,
1658-
withTransition, flow, selectedCircles) {
1659-
var size = this.pointR(element) * 3.0;
1660-
var halfSize = size * 0.5;
1661-
1662-
function getPoints(d) {
1663-
var x1 = xPosFn(d);
1664-
var y1 = yPosFn(d) - halfSize;
1665-
var x2 = x1 - halfSize;
1666-
var y2 = y1 + halfSize;
1667-
var x3 = x1;
1668-
var y3 = y2 + halfSize;
1669-
var x4 = x1 + halfSize;
1670-
var y4 = y2;
1671-
1672-
return [x1, y1, x2, y2, x3, y3, x4, y4].join(" ");
1673-
}
1674-
1675-
// style the custom element added
1676-
return element
1677-
.attr("points", getPoints)
1678-
.style("opacity", opacityStyleFn)
1679-
.style("fill", fillStyleFn);
1680-
}
1681-
}
1624+
pattern: [
1625+
"<polygon points='2.5 0 0 2.5 2.5 5 5 2.5 2.5 0'></polygon>"
1626+
]
1627+
}
1628+
}
1629+
},
1630+
CustomPointsHearts: {
1631+
options: {
1632+
data: {
1633+
columns: [
1634+
['data1', 100, 400, 1000, 900, 500],
1635+
['data2', 20, 40, 500, 300, 200]
1636+
]
1637+
},
1638+
point: {
1639+
pattern: [
1640+
"<path d='m3.937502,2.348755c1.314192,-3.618047 6.463238,0 0,4.651779c-6.463238,-4.651779 -1.314192,-8.269826 0,-4.651779z' />"
1641+
]
1642+
}
1643+
}
1644+
},
1645+
CombinationPoints: {
1646+
options: {
1647+
data: {
1648+
columns: [
1649+
['data1', 100, 400, 1000, 900, 500],
1650+
['data2', 20, 40, 500, 300, 200],
1651+
['data3', 80, 350, 800, 450, 500],
1652+
['data4', 150, 240, 300, 700, 300]
1653+
]
1654+
},
1655+
point: {
1656+
pattern: [
1657+
"circle",
1658+
"rectangle",
1659+
"<polygon points='2.5 0 0 2.5 2.5 5 5 2.5 2.5 0'></polygon>",
1660+
"<polygon points='2.5 0 0 5 5 5'></polygon>"
1661+
]
16821662
}
16831663
}
16841664
}

spec/interaction-spec.js

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ describe("INTERACTION", () => {
163163
});
164164

165165
it("set option point.type='rectangle'", () => {
166-
args.point.type = "rectangle";
166+
args.point.pattern = ["rectangle"];
167167
clicked = false;
168168
data = null;
169169
});
@@ -183,34 +183,9 @@ describe("INTERACTION", () => {
183183
});
184184

185185
it("set option point.type=polygon(custom triangle)", () => {
186-
args.point.type = {
187-
create: function(element, cssClassFn, sizeFn, fillStyleFn) {
188-
return element.enter().append("polygon")
189-
.attr("class", cssClassFn)
190-
.style("fill", fillStyleFn);
191-
},
192-
update: function(element, xPosFn, yPosFn, opacityStyleFn, fillStyleFn,
193-
withTransition, flow, selectedCircles) {
194-
var size = this.pointR(element) * 3.0;
195-
var halfSize = size * 0.5;
196-
197-
function getPoints(d) {
198-
var x1 = xPosFn(d);
199-
var y1 = yPosFn(d) - halfSize;
200-
var x2 = x1 - halfSize;
201-
var y2 = y1 + size;
202-
var x3 = x1 + halfSize;
203-
var y3 = y2;
204-
205-
return [x1, y1, x2, y2, x3, y3].join(" ");
206-
}
207-
208-
return element
209-
.attr("points", getPoints)
210-
.style("opacity", opacityStyleFn)
211-
.style("fill", fillStyleFn);
212-
}
213-
};
186+
args.point.pattern = [
187+
"<polygon points='5 2.5 2.5 5 7.5 5'></polygon>"
188+
];
214189

215190
clicked = false;
216191
data = null;
@@ -219,20 +194,20 @@ describe("INTERACTION", () => {
219194
it("check for data click for polygon data point", () => {
220195
const main = chart.internal.main;
221196
const rect = main.select(`.${CLASS.eventRect}.${CLASS.eventRect}`).node();
222-
const circle = main.select(`.${CLASS.circles}-data2 polygon`).node().getBBox();
197+
const circle = main.select(`.${CLASS.circles}-data2 use`).node().getBBox();
223198

224199
util.fireEvent(rect, "click", {
225200
clientX: circle.x,
226201
clientY: circle.y
227202
}, chart);
228203

229-
expect(clicked).to.be.true;
230-
expect(data.value).to.be.equal(20);
204+
expect(clicked).to.be.false;
205+
expect(data).to.be.equal(null);
231206
});
232207

233208
it("set option data.type='area'", () => {
234209
args.data.type = "area";
235-
args.point.type = "circle";
210+
args.point.pattern = ["circle"];
236211

237212
clicked = false;
238213
data = null;

spec/shape.point-spec.js

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ describe("SHAPE POINT", () => {
5151
]
5252
},
5353
point: {
54-
type: "rectangle"
54+
pattern: ["rectangle"]
5555
}
5656
};
5757
});
@@ -76,46 +76,17 @@ describe("SHAPE POINT", () => {
7676
]
7777
},
7878
point: {
79-
type: {
80-
create(element, cssClassFn, sizeFn, fillStyle) {
81-
return element.enter().append("polygon")
82-
.attr("class", cssClassFn)
83-
.style("fill", fillStyle);
84-
},
85-
86-
update(element, xPosFn, yPosFn, opacityStyleFn, fillStyleFn,
87-
withTransition, flow, selectedCircles) {
88-
let mainCircles;
89-
const triangleSize = 10;
90-
91-
function getPoints(d) {
92-
const x1 = xPosFn(d);
93-
const y1 = yPosFn(d) - (triangleSize * 0.5);
94-
const x2 = x1 - (triangleSize * 0.5);
95-
const y2 = y1 + (triangleSize * 0.5);
96-
const x3 = x1;
97-
const y3 = y2 + (triangleSize * 0.5);
98-
const x4 = x1 + (triangleSize * 0.5);
99-
const y4 = y2;
100-
return `${x1} ${y1} ${x2} ${y2} ${x3} ${y3} ${x4} ${y4}`;
101-
}
102-
103-
mainCircles = element
104-
.attr("points", getPoints)
105-
.style("opacity", opacityStyleFn)
106-
.style("fill", fillStyleFn);
107-
108-
return mainCircles;
109-
}
110-
}
79+
pattern: [
80+
"<polygon points='5 2.5 2.5 5 7.5 5'></polygon>"
81+
]
11182
}
11283
};
11384
});
11485

115-
it("Should render svg polygon elements", () => {
86+
it("Should render svg \"use\" elements", () => {
11687
const target = chart.internal.svg.select(".bb-chart-line.bb-target-data1");
11788
const circlesEl = target.select(".bb-circles-data1").node();
118-
const polygons = circlesEl.getElementsByTagName("polygon");
89+
const polygons = circlesEl.getElementsByTagName("use");
11990

12091
expect(polygons.length).to.be.equal(6);
12192
});

src/api/api.flow.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,9 @@ extend(ChartInternal.prototype, {
406406

407407
mainCircle
408408
.attr("x", xFunc)
409-
.attr("y", yFunc);
409+
.attr("y", yFunc)
410+
.attr("cx", cx) // when pattern is used, it possibly contain 'circle' also.
411+
.attr("cy", cy);
410412
}
411413

412414
mainText

src/config/Options.js

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2087,14 +2087,23 @@ export default class Options {
20872087
* @property {Boolean} [point.focus.expand.r=point.r*1.75] The radius size of each point on focus.<br>
20882088
* - **Note:** For 'bubble' type, the default is `bubbleSize*1.15`
20892089
* @property {Number} [point.select.r=point.r*4] The radius size of each point on selected.
2090-
* @property {String|Object} [point.type="circle"] The type of point to be drawn<br>
2091-
* - **Note:** If chart has 'bubble' type, only circle can be used.<br>
2090+
* @property {String} [point.type="circle"] The type of point to be drawn<br>
2091+
* - **Note:**
2092+
* - If chart has 'bubble' type, only circle can be used.
2093+
* - For IE, non circle point expansions are not supported due to lack of transform support.
20922094
* - **Available Values:**
20932095
* - circle
20942096
* - rectangle
2095-
*
2096-
* @property {Function} [point.type.create] If specified will be invoked to create data points, this function must return a d3 selection.
2097-
* @property {Function} [point.type.update] If specified will be invoked to update data points, this function must return a d3 selection.
2097+
* @property {Array} [point.pattern=[]] The type of point or svg shape as string, to be drawn for each line<br>
2098+
* - **Note:**
2099+
* - This is an `experimental` feature and can have some unexpected behaviors.
2100+
* - If chart has 'bubble' type, only circle can be used.
2101+
* - For IE, non circle point expansions are not supported due to lack of transform support.
2102+
* - **Available Values:**
2103+
* - circle
2104+
* - rectangle
2105+
* - svg shape tag interpreted as string<br>
2106+
* (ex. `<polygon points='2.5 0 0 5 5 5'></polygon>`)
20982107
* @example
20992108
* point: {
21002109
* show: false,
@@ -2116,29 +2125,23 @@ export default class Options {
21162125
* r: 3
21172126
* },
21182127
*
2128+
* // valid values are "circle" or "rectangle"
21192129
* type: "rectangle",
21202130
*
2121-
* // or for custom shapes you can use an object with a "create" and "update" functions
2122-
* type: {
2123-
* // to create a custom type, set create & update functions as well
2124-
* create: function(element, cssClassFn, sizeFn, fillStyleFn) {
2125-
* // should create node element to be used as data point and must return a d3.selection
2126-
* ...
2127-
* return element;
2128-
* },
2129-
* update: function(element, xPosFn, yPosFn, opacityStyleFn, fillStyleFn, withTransition, flow, selectedCircles) {
2130-
* // adjust the position & styles to the given element and must return a d3.selection
2131-
* ...
2132-
* return element;
2133-
* }
2134-
* }
2131+
* // or indicate as pattern
2132+
* pattern: [
2133+
* "circle",
2134+
* "rectangle",
2135+
* "<polygon points='0 6 4 0 -4 0'></polygon>"
2136+
* ],
21352137
* }
21362138
*/
21372139
point_show: true,
21382140
point_r: 2.5,
21392141
point_sensitivity: 10,
21402142
point_focus_expand_enabled: true,
21412143
point_focus_expand_r: undefined,
2144+
point_pattern: [],
21422145
point_select_r: undefined,
21432146
point_type: "circle",
21442147

src/internals/ChartInternal.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ export default class ChartInternal {
8282
const config = $$.config;
8383

8484
// MEMO: clipId needs to be unique because it conflicts when multiple charts exist
85-
$$.clipId = `bb-${+new Date()}-clip`;
85+
$$.datetimeId = `bb-${+new Date()}`;
86+
$$.clipId = `${$$.datetimeId}-clip`;
8687
$$.clipIdForXAxis = `${$$.clipId}-xaxis`;
8788
$$.clipIdForYAxis = `${$$.clipId}-yaxis`;
8889
$$.clipIdForGrid = `${$$.clipId}-grid`;
@@ -101,6 +102,7 @@ export default class ChartInternal {
101102

102103
$$.color = $$.generateColor();
103104
$$.levelColor = $$.generateLevelColor();
105+
$$.point = $$.generatePoint();
104106

105107
$$.extraLineClasses = $$.generateExtraLineClass();
106108

@@ -257,17 +259,17 @@ export default class ChartInternal {
257259
$$.svg.attr("class", config.svg_classname);
258260

259261
// Define defs
260-
const defs = $$.svg.append("defs");
262+
$$.defs = $$.svg.append("defs");
261263

262-
$$.clipChart = $$.appendClip(defs, $$.clipId);
263-
$$.clipXAxis = $$.appendClip(defs, $$.clipIdForXAxis);
264-
$$.clipYAxis = $$.appendClip(defs, $$.clipIdForYAxis);
265-
$$.clipGrid = $$.appendClip(defs, $$.clipIdForGrid);
266-
$$.clipSubchart = $$.appendClip(defs, $$.clipIdForSubchart);
264+
$$.clipChart = $$.appendClip($$.defs, $$.clipId);
265+
$$.clipXAxis = $$.appendClip($$.defs, $$.clipIdForXAxis);
266+
$$.clipYAxis = $$.appendClip($$.defs, $$.clipIdForYAxis);
267+
$$.clipGrid = $$.appendClip($$.defs, $$.clipIdForGrid);
268+
$$.clipSubchart = $$.appendClip($$.defs, $$.clipIdForSubchart);
267269

268270
// set color patterns
269271
if (isFunction(config.color_tiles) && $$.patterns) {
270-
$$.patterns.forEach(p => defs.append(() => p.node));
272+
$$.patterns.forEach(p => $$.defs.append(() => p.node));
271273
}
272274

273275
$$.updateSvgSize();

src/internals/type.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,11 @@ extend(ChartInternal.prototype, {
112112

113113
// determine if is 'circle' data point
114114
isCirclePoint() {
115-
return this.config.point_type === "circle";
115+
const config = this.config;
116+
const pattern = config.point_pattern;
117+
118+
return config.point_type === "circle" &&
119+
(!pattern || (isArray(pattern) && pattern.length === 0));
116120
},
117121

118122
lineData(d) {

0 commit comments

Comments
 (0)