Skip to content

Commit 5d0fd8f

Browse files
committed
Implement Style#windingRule and use it in Path#contains().
1 parent 4f27be8 commit 5d0fd8f

5 files changed

Lines changed: 27 additions & 22 deletions

File tree

src/item/Item.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2185,12 +2185,22 @@ var Item = Base.extend(Callback, /** @lends Item# */{
21852185
* miterLimit imposes a limit on the ratio of the miter length to the
21862186
* {@link Item#strokeWidth}.
21872187
*
2188-
* @default 10
21892188
* @property
21902189
* @name Item#miterLimit
2190+
* @default 10
21912191
* @type Number
21922192
*/
21932193

2194+
/**
2195+
* The winding-rule with which the shape gets filled. Please note that only
2196+
* modern browsers support winding-rules other than {@code 'nonzero'}.
2197+
*
2198+
* @property
2199+
* @name Item#windingRule
2200+
* @default 'nonzero'
2201+
* @type String('nonzero', 'evenodd')
2202+
*/
2203+
21942204
/**
21952205
* {@grouptitle Fill Style}
21962206
*

src/item/Shape.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ var Shape = Item.extend(/** @lends Shape# */{
192192
if (!clip && (fillColor || strokeColor)) {
193193
this._setStyles(ctx);
194194
if (fillColor)
195-
ctx.fill();
195+
ctx.fill(style.getWindingRule());
196196
if (strokeColor)
197197
ctx.stroke();
198198
}

src/path/CompoundPath.js

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -203,39 +203,36 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
203203
ctx.beginPath();
204204
for (var i = 0, l = children.length; i < l; i++)
205205
children[i]._draw(ctx, param);
206-
var res = ctx.isPointInPath(point.x, point.y);
206+
var res = ctx.isPointInPath(point.x, point.y, this.getWindingRule());
207207
CanvasProvider.release(ctx);
208208
return res && children;
209209
/*#*/ } // options.nativeContains
210210

211211
// Compound paths are a little complex: In order to determine whether a
212-
// point is inside a path or not due to the even-odd rule, we need to
212+
// point is inside a path or not due to the winding rule, we need to
213213
// check all the children and count how many intersect. If it's an odd
214214
// number, the point is inside the path. Once we know it's inside the
215215
// path, _hitTest also needs access to the first intersecting element,
216-
// for the HitResult, so we collect and return a list here.
216+
// for the HitResult, so we return it here.
217217
var total = 0,
218-
children = [];
218+
first = null,
219+
evenOdd = this.getWindingRule() === 'evenodd';
219220
for (var i = 0, l = this._children.length; i < l; i++) {
220221
var child = this._children[i],
221222
winding = child._getWinding(point);
222223
total += winding;
223-
/*
224-
if (winding & 1)
225-
children.push(child);
226-
*/
227-
if (winding)
228-
children.push(child);
224+
if (!first && (evenOdd ? winding & 1 : winding))
225+
first = child;
229226
}
230-
return total && children; // <- non-zero // even-odd: (total & 1) && children;
227+
return (evenOdd ? total & 1 : total) && first;
231228
},
232229

233230
_hitTest: function _hitTest(point, options) {
234231
var res = _hitTest.base.call(this, point,
235232
Base.merge(options, { fill: false }));
236233
if (!res && options.fill && this.hasFill()) {
237234
res = this._contains(point);
238-
res = res ? new HitResult('fill', res[0]) : null;
235+
res = res ? new HitResult('fill', res) : null;
239236
}
240237
return res;
241238
},
@@ -253,7 +250,7 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
253250
if (!param.clip) {
254251
this._setStyles(ctx);
255252
if (style.getFillColor())
256-
ctx.fill();
253+
ctx.fill(style.getWindingRule());
257254
if (style.getStrokeColor())
258255
ctx.stroke();
259256
}

src/path/Path.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,15 +1732,12 @@ var Path = PathItem.extend(/** @lends Path# */{
17321732
// To compare with native canvas approach:
17331733
var ctx = CanvasProvider.getContext(1, 1);
17341734
this._draw(ctx, Base.merge({ clip: true }));
1735-
var res = ctx.isPointInPath(point.x, point.y);
1735+
var res = ctx.isPointInPath(point.x, point.y, this.getWindingRule());
17361736
CanvasProvider.release(ctx);
17371737
return res;
17381738
/*#*/ } // options.nativeContains
1739-
1740-
// even-odd:
1741-
// return !!(this._getWinding(point) & 1);
1742-
// non-zero:
1743-
return !!this._getWinding(point);
1739+
var winding = this._getWinding(point);
1740+
return !!(this.getWindingRule() == 'evenodd' ? winding & 1 : winding);
17441741
},
17451742

17461743
_hitTest: function(point, options) {
@@ -2034,7 +2031,7 @@ var Path = PathItem.extend(/** @lends Path# */{
20342031
// or stroke, there is no need to continue.
20352032
this._setStyles(ctx);
20362033
if (fillColor)
2037-
ctx.fill();
2034+
ctx.fill(style.getWindingRule());
20382035
if (strokeColor) {
20392036
if (dashLength) {
20402037
// We cannot use the path created by drawSegments above

src/style/Style.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ var Style = Base.extend(new function() {
7878
miterLimit: 10,
7979
dashOffset: 0,
8080
dashArray: [],
81+
windingRule: 'nonzero',
8182
// Shadows
8283
shadowColor: undefined,
8384
shadowBlur: 0,

0 commit comments

Comments
 (0)