Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perPixelTargetFind not working in nested group. #5287

Merged
merged 12 commits into from
Oct 13, 2018
25 changes: 18 additions & 7 deletions src/canvas.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -1194,15 +1194,20 @@
},

/**
* Checks point is inside the object.
* @param {Object} [pointer] x,y object of point coordinates we want to check.
* @param {fabric.Object} obj Object to test against
* @param {Object} [globalPointer] x,y object of point coordinates relative to canvas used to search per pixel target.
* @return {Boolean} true if point is contained within an area of given object
* @private
*/
_checkTarget: function(pointer, obj) {
_checkTarget: function(pointer, obj, globalPointer) {
if (obj &&
obj.visible &&
obj.evented &&
this.containsPoint(null, obj, pointer)){
if ((this.perPixelTargetFind || obj.perPixelTargetFind) && !obj.isEditing) {
var isTransparent = this.isTargetTransparent(obj, pointer.x, pointer.y);
var isTransparent = this.isTargetTransparent(obj, globalPointer.x, globalPointer.y);
if (!isTransparent) {
return true;
}
Expand All @@ -1214,20 +1219,26 @@
},

/**
* Function used to search inside objects an object that contains pointer in bounding box or that contains pointerOnCanvas when painted
* @param {Array} [objects] objects array to look into
* @param {Object} [pointer] x,y object of point coordinates we want to check.
* @return {fabric.Object} object that contains pointer
* @private
*/
_searchPossibleTargets: function(objects, pointer) {

// Cache all targets where their bounding box contains point.
var target, i = objects.length, normalizedPointer, subTarget;
var target, i = objects.length, subTarget;
// Do not check for currently grouped objects, since we check the parent group itself.
// until we call this function specifically to search inside the activeGroup
while (i--) {
if (this._checkTarget(pointer, objects[i])) {
var objToCheck = objects[i];
if (this._checkTarget(objToCheck.group && objToCheck.group.type !== 'activeSelection'
? this._normalizePointer(objToCheck.group, pointer)
: pointer,
objToCheck, pointer)) {
target = objects[i];
if (target.subTargetCheck && target instanceof fabric.Group) {
normalizedPointer = this._normalizePointer(target, pointer);
subTarget = this._searchPossibleTargets(target._objects, normalizedPointer);
subTarget = this._searchPossibleTargets(target._objects, pointer);
subTarget && this.targets.push(subTarget);
}
break;
Expand Down
83 changes: 83 additions & 0 deletions test/unit/canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,89 @@
canvas.remove(triangle);
});

QUnit.test('findTarget with perPixelTargetFind in nested group', function(assert) {
assert.ok(typeof canvas.findTarget === 'function');
var triangle = makeTriangle({ left: 0, top: 0, width: 30, height: 30, fill: 'yellow' }),
triangle2 = makeTriangle({ left: 100, top: 120, width: 30, height: 30, angle: 100, fill: 'pink' }),
circle = new fabric.Circle({ radius: 30, top: 0, left: 30, fill: 'blue' }),
circle2 = new fabric.Circle({ scaleX: 2, scaleY: 2, radius: 10, top: 120, left: -20, fill: 'purple' }),
rect = new fabric.Rect({ width: 100, height: 80, top: 50, left: 60, fill: 'green' }),
rect2 = new fabric.Rect({ width: 50, height: 30, top: 10, left: 110, fill: 'red', skewX: 40, skewY: 20 }),
group1 = new fabric.Group([triangle, circle, rect2], { subTargetCheck: true }),
group2 = new fabric.Group([group1, circle2, rect, triangle2], { subTargetCheck: true }),
group3 = new fabric.Group([group2], { subTargetCheck: true }),
target;

canvas.add(group3);
canvas.perPixelTargetFind = true;
target = canvas.findTarget({
clientX: 5, clientY: 5
});
assert.equal(target, null, 'Should return null because of transparency checks case 1');
target = canvas.findTarget({
clientX: 21, clientY: 9
});
assert.equal(target, null, 'Should return null because of transparency checks case 2');
target = canvas.findTarget({
clientX: 37, clientY: 7
});
assert.equal(target, null, 'Should return null because of transparency checks case 3');
target = canvas.findTarget({
clientX: 89, clientY: 47
});
assert.equal(target, null, 'Should return null because of transparency checks case 4');
target = canvas.findTarget({
clientX: 16, clientY: 122
});
assert.equal(target, null, 'Should return null because of transparency checks case 5');
target = canvas.findTarget({
clientX: 127, clientY: 37
});
assert.equal(target, null, 'Should return null because of transparency checks case 6');
target = canvas.findTarget({
clientX: 87, clientY: 139
});
assert.equal(target, null, 'Should return null because of transparency checks case 7');
target = canvas.findTarget({
clientX: 15, clientY: 15
});
assert.equal(target, group3, 'Should return the group3 now');
assert.equal(canvas.targets.length, 3, 'Subtargets length should be 3');
assert.equal(canvas.targets[0], triangle, 'The deepest target should be triangle');
target = canvas.findTarget({
clientX: 50, clientY: 20
});
assert.equal(target, group3, 'Should return the group3 now');
assert.equal(canvas.targets.length, 3, 'Subtargets length should be 3');
assert.equal(canvas.targets[0], circle, 'The deepest target should be circle');
target = canvas.findTarget({
clientX: 117, clientY: 16
});
assert.equal(target, group3, 'Should return the group3 now');
assert.equal(canvas.targets.length, 3, 'Subtargets length should be 2');
assert.equal(canvas.targets[0], rect2, 'The deepest target should be rect2');
target = canvas.findTarget({
clientX: 100, clientY: 90
});
assert.equal(target, group3, 'Should return the group3 now');
assert.equal(canvas.targets.length, 2, 'Subtargets length should be 2');
assert.equal(canvas.targets[0], rect, 'The deepest target should be rect');
target = canvas.findTarget({
clientX: 9, clientY: 145
});
assert.equal(target, group3, 'Should return the group3 now');
assert.equal(canvas.targets.length, 2, 'Subtargets length should be 2');
assert.equal(canvas.targets[0], circle2, 'The deepest target should be circle2');
target = canvas.findTarget({
clientX: 66, clientY: 143
});
assert.equal(target, group3, 'Should return the group3 now');
assert.equal(canvas.targets.length, 2, 'Subtargets length should be 2');
assert.equal(canvas.targets[0], triangle2, 'The deepest target should be triangle2');
canvas.perPixelTargetFind = false;
canvas.remove(group3);
});

QUnit.test('findTarget on activegroup', function(assert) {
var rect1 = makeRect({ left: 0, top: 0 }), target;
var rect2 = makeRect({ left: 20, top: 20 });
Expand Down