Permalink
Browse files

finished pixel detection algo which can be applied to any Shape. This…

… enables a lot of new functionality, such as image pixel detection, shape border detection, and custom shape detection that isn't drawn with a standard path
  • Loading branch information...
ericdrowell committed Apr 1, 2012
1 parent 08e2b74 commit 95fabe9160d3c95ed25edfb4de04c89cc4b6edce
Showing with 140 additions and 17 deletions.
  1. +35 −8 dist/kinetic-core.js
  2. +1 −1 dist/kinetic-core.min.js
  3. +32 −5 src/Shape.js
  4. +3 −3 src/Stage.js
  5. +69 −0 tests/js/functionalTests.js
  6. BIN tests/lion.png
View
@@ -1280,7 +1280,7 @@ Kinetic.Stage.prototype = {
this.targetFound = true;
}
- if(shape.visible && pos !== undefined && shape._isPointInPath(pos)) {
+ if(shape.visible && pos !== undefined && shape._isPointInShape(pos)) {
// handle onmousedown
if(!isDragging && this.mouseDown) {
this.mouseDown = false;
@@ -1482,8 +1482,8 @@ Kinetic.Stage.prototype = {
* clear default layers
*/
_clearDefaultLayers: function() {
- var pathLayer = this.pathLayer;
- pathLayer.clear();
+ this.bufferLayer.clear();
+ this.pathLayer.clear();
},
/**
* begin listening for events by adding event handlers
@@ -1902,6 +1902,8 @@ Kinetic.GlobalObject.extend(Kinetic.Group, Kinetic.Node);
* @config {Number} [strokeWidth] stroke width
* @config {String} [lineJoin] line join. Can be "miter", "round", or "bevel". The default
* is "miter"
+ * @config {String} [detectionType] shape detection type. Can be "path" or "pixel".
+ * The default is "path" because it performs better
*/
Kinetic.Shape = function(config) {
this.className = 'Shape';
@@ -1916,6 +1918,10 @@ Kinetic.Shape = function(config) {
}
}
+ if(config.detectionType === undefined) {
+ config.detectionType = 'path';
+ }
+
// required
this.drawFunc = config.drawFunc;
@@ -2066,12 +2072,33 @@ Kinetic.Shape.prototype = {
* custom isPointInPath method which can use path detection
* or pixel detection
*/
- _isPointInPath: function(pos) {
+ _isPointInShape: function(pos) {
var stage = this.getStage();
- var pathLayer = stage.pathLayer;
- var pathLayerContext = pathLayer.getContext();
- this._draw(pathLayer);
- return pathLayerContext.isPointInPath(pos.x, pos.y);
+
+ if(this.detectionType === 'path') {
+ var pathLayer = stage.pathLayer;
+ var pathLayerContext = pathLayer.getContext();
+
+ this._draw(pathLayer);
+
+ return pathLayerContext.isPointInPath(pos.x, pos.y);
+ }
+ else {
+ var bufferLayer = stage.bufferLayer;
+ var bufferLayerContext = bufferLayer.getContext();
+
+ this._draw(bufferLayer);
+
+ var w = stage.width;
+ var h = stage.height;
+ var x = pos.x;
+ var y = pos.y;
+ var imageData = bufferLayerContext.getImageData(0, 0, w, h);
+ var data = imageData.data;
+ var alpha = data[((w * y) + x) * 4 + 3];
+
+ return (alpha !== undefined && alpha !== 0);
+ }
}
};
// extend Node
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -12,6 +12,8 @@
* @config {Number} [strokeWidth] stroke width
* @config {String} [lineJoin] line join. Can be "miter", "round", or "bevel". The default
* is "miter"
+ * @config {String} [detectionType] shape detection type. Can be "path" or "pixel".
+ * The default is "path" because it performs better
*/
Kinetic.Shape = function(config) {
this.className = 'Shape';
@@ -26,6 +28,10 @@ Kinetic.Shape = function(config) {
}
}
+ if(config.detectionType === undefined) {
+ config.detectionType = 'path';
+ }
+
// required
this.drawFunc = config.drawFunc;
@@ -176,12 +182,33 @@ Kinetic.Shape.prototype = {
* custom isPointInPath method which can use path detection
* or pixel detection
*/
- _isPointInPath: function(pos) {
+ _isPointInShape: function(pos) {
var stage = this.getStage();
- var pathLayer = stage.pathLayer;
- var pathLayerContext = pathLayer.getContext();
- this._draw(pathLayer);
- return pathLayerContext.isPointInPath(pos.x, pos.y);
+
+ if(this.detectionType === 'path') {
+ var pathLayer = stage.pathLayer;
+ var pathLayerContext = pathLayer.getContext();
+
+ this._draw(pathLayer);
+
+ return pathLayerContext.isPointInPath(pos.x, pos.y);
+ }
+ else {
+ var bufferLayer = stage.bufferLayer;
+ var bufferLayerContext = bufferLayer.getContext();
+
+ this._draw(bufferLayer);
+
+ var w = stage.width;
+ var h = stage.height;
+ var x = pos.x;
+ var y = pos.y;
+ var imageData = bufferLayerContext.getImageData(0, 0, w, h);
+ var data = imageData.data;
+ var alpha = data[((w * y) + x) * 4 + 3];
+
+ return (alpha !== undefined && alpha !== 0);
+ }
}
};
// extend Node
View
@@ -254,7 +254,7 @@ Kinetic.Stage.prototype = {
this.targetFound = true;
}
- if(shape.visible && pos !== undefined && shape._isPointInPath(pos)) {
+ if(shape.visible && pos !== undefined && shape._isPointInShape(pos)) {
// handle onmousedown
if(!isDragging && this.mouseDown) {
this.mouseDown = false;
@@ -456,8 +456,8 @@ Kinetic.Stage.prototype = {
* clear default layers
*/
_clearDefaultLayers: function() {
- var pathLayer = this.pathLayer;
- pathLayer.clear();
+ this.bufferLayer.clear();
+ this.pathLayer.clear();
},
/**
* begin listening for events by adding event handlers
@@ -519,6 +519,75 @@ Test.prototype.tests = {
};
imageObj.src = '../darth-vader.jpg';
},
+ /*
+ * WARNING: this functional test will only pass if it's hosted on
+ * a webserver due to cross domain security issues
+ */
+ 'EVENTS - image pixel detection': function(containerId) {
+ var imageObj = new Image();
+ imageObj.onload = function() {
+ var stage = new Kinetic.Stage({
+ container: containerId,
+ width: 578,
+ height: 200
+ });
+ var layer = new Kinetic.Layer();
+ var darth = new Kinetic.Image({
+ x: 200,
+ y: 40,
+ image: imageObj,
+ detectionType: 'pixel',
+ draggable: true
+ });
+
+ darth.on('mouseover', function() {
+ log('mouseover');
+ });
+
+ darth.on('mouseout', function() {
+ log('mouseout');
+ });
+
+ layer.add(darth);
+ stage.add(layer);
+ };
+ imageObj.src = '../lion.png';
+ },
+ 'EVENTS - star pixel detection': function(containerId) {
+ var imageObj = new Image();
+ imageObj.onload = function() {
+ var stage = new Kinetic.Stage({
+ container: containerId,
+ width: 578,
+ height: 200
+ });
+ var layer = new Kinetic.Layer();
+ var star = new Kinetic.Star({
+ x: 200,
+ y: 100,
+ points: 10,
+ innerRadius: 40,
+ outerRadius: 70,
+ fill: 'green',
+ stroke: 'blue',
+ strokeWidth: 20,
+ detectionType: 'pixel',
+ draggable: true
+ });
+
+ star.on('mouseover', function() {
+ log('mouseover');
+ });
+
+ star.on('mouseout', function() {
+ log('mouseout');
+ });
+
+ layer.add(star);
+ stage.add(layer);
+ };
+ imageObj.src = '../lion.png';
+ },
'EVENTS - drag events click': function(containerId) {
var stage = new Kinetic.Stage({
container: containerId,
View
Binary file not shown.

1 comment on commit 95fabe9

@ericdrowell

This comment has been minimized.

Show comment Hide comment
@ericdrowell

ericdrowell Apr 1, 2012

Owner

yes, a security error will be thrown if a developer uses the "pixel" detectionType on images not from on their domain. They will need to do defensive coding on their end because KineticJS won't know where the images are coming from. I don't want to use try/catch because the devs need to know when a problem occurs

Owner

ericdrowell commented on 95fabe9 Apr 1, 2012

yes, a security error will be thrown if a developer uses the "pixel" detectionType on images not from on their domain. They will need to do defensive coding on their end because KineticJS won't know where the images are coming from. I don't want to use try/catch because the devs need to know when a problem occurs

Please sign in to comment.