diff --git a/js/src/selectors.js b/js/src/selectors.js index 011a20c5..dad3ba36 100644 --- a/js/src/selectors.js +++ b/js/src/selectors.js @@ -3,13 +3,99 @@ var _scale_point = function (xy, width, height) { return [xy[0] / width * 2 - 1, 1 - xy[1] / height * 2]; }; +function points_in_lasso (vertx, verty, x, y) { + let meanx = 0; + let meany = 0; + let N = x.length; + let Nvert = vertx.length; + for (let i = 0; i < Nvert; i++) { + meanx += vertx[i]; + meany += verty[i]; + } + meanx /= Nvert; + meany /= Nvert; + let radius_squared = 0; + for (let i = 0; i < Nvert; i++) { + let rx = vertx[i] - meanx; + let ry = verty[i] - meany; + radius_squared = Math.max(radius_squared, rx*rx + ry*ry); + } + let mask = new Uint8Array(N); + let indices = new Uint32Array(N); + let count = 0; + for (let k = 0; k < N; k++) { + let testx = x[k]; + let testy = y[k]; + let i, j; + let is_inside = false; + mask[k] = 0; + let distancesq = Math.pow(testx - meanx, 2) + Math.pow(testy - meany, 2); + if (distancesq < radius_squared) { + for (i = 0, j = Nvert-1; i < Nvert; j = i++) { + if (((verty[i]>testy) !== (verty[j]>testy)) && + (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i])) { + is_inside = !is_inside; + } + } + mask[k] = is_inside ? 1 : 0; + if (is_inside) { + indices[count++] = k; + } + } + } + indices = indices.slice(0, count); + return { mask: mask, indices: indices }; +} + +export +class Selector { + points_inside (x, y) { + const N = x.length; + let mask = new Uint8Array(N); + for (let k = 0; k < N; k++) { + mask[k] = this.point_inside(x[k], y[k]) ? 1 : 0; + } + return mask; + } + indices_inside (x, y) { + const N = x.length; + let indices = new Uint32Array(N); + let count = 0; + for (let k = 0; k < N; k++) { + if (this.point_inside(x[k], y[k])) { + indices[count++] = k; + } + } + indices = indices.slice(0, count); + return indices; + } +}; + export -class LassoSelector { +class LassoSelector extends Selector { constructor (canvas) { + super(); this.canvas = canvas; this.points = []; } - + _get_vertices () { + return { vx: this.points.map(p => p[0]), vy: this.points.map(p => p[1]) }; + } + point_inside (x, y) { + let { vx, vy } = this._get_vertices(); + let { mask } = points_in_lasso(vx, vy, [x], [y]); + return mask[0] === 1; + } + points_inside (x, y) { + let { vx, vy } = this._get_vertices(); + let { mask } = points_in_lasso(vx, vy, x, y); + return mask; + } + indices_inside (x, y) { + let { vx, vy } = this._get_vertices(); + let { indices } = points_in_lasso(vx, vy, x, y); + return indices; + } mouseMove (x, y) { this.points.push([x, y]); } @@ -40,13 +126,24 @@ class LassoSelector { } export -class CircleSelector { +class CircleSelector extends Selector { constructor (canvas) { + super(); this.canvas = canvas; this.points = []; this.begin = null; this.end = null; } + point_inside (x, y) { + if (this.begin === null || this.end === null) { + return false; + } + let [x1, y1] = this.begin; + let [x2, y2] = this.end; + let radius_squared = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2); + let distance_squared = Math.pow(x - x1, 2) + Math.pow(y - y1, 2); + return distance_squared < radius_squared; + } mouseMove (x, y) { if (!this.begin) { this.begin = [x, y]; @@ -85,13 +182,26 @@ class CircleSelector { } export -class RectangleSelector { +class RectangleSelector extends Selector { constructor (canvas) { + super(); this.canvas = canvas; this.points = []; this.begin = null; this.end = null; } + point_inside (x, y) { + if (this.begin === null || this.end === null) { + return false; + } + let [x1, y1] = this.begin; + let [x2, y2] = this.end; + let xmin = Math.min(x1, x2); + let ymin = Math.min(y1, y2); + let xmax = Math.max(x1, x2); + let ymax = Math.max(y1, y2); + return (x > xmin) && (x < xmax) && (y > ymin) && (y < ymax); + } mouseMove (x, y) { if (!this.begin) { this.begin = [x, y]; diff --git a/js/test/selectors.ts b/js/test/selectors.ts index c37fb0dd..39752f41 100644 --- a/js/test/selectors.ts +++ b/js/test/selectors.ts @@ -25,6 +25,11 @@ describe("Selector", () => { expect(path_data['device']).to.deep.equals([[-1, 1], [1, 1], [-1, -1]]) expect(path_data['type']).to.equal('lasso'); + expect(selector.point_inside(2, 2)).to.be.true; + expect(selector.point_inside(10, 10)).to.be.false; + expect([...selector.points_inside([2, 10], [2, 10])]).to.deep.equals([1, 0]) + expect([...selector.indices_inside([2, 10], [2, 10])]).to.deep.equals([0]) + selector.close() data_inside = context.getImageData(2,2,1,1) data_outside = context.getImageData(10,10,1,1) @@ -52,6 +57,11 @@ describe("Selector", () => { expect(path_data['device']['end']).to.deep.equals([0, 1]) expect(path_data['type']).to.equal('circle'); + expect(selector.point_inside(2, 2)).to.be.true; + expect(selector.point_inside(10, 10)).to.be.false; + expect([...selector.points_inside([2, 10], [2, 10])]).to.deep.equals([1, 0]) + expect([...selector.indices_inside([2, 10], [2, 10])]).to.deep.equals([0]) + selector.mouseMove(0, 10); path_data = selector.getData(10, 10) expect(path_data['pixel']['begin']).to.deep.equals([0, 0]) @@ -87,6 +97,11 @@ describe("Selector", () => { expect(path_data['device']['end']).to.deep.equals([0, 0]) expect(path_data['type']).to.equal('rectangle'); + expect(selector.point_inside(2, 2)).to.be.true; + expect(selector.point_inside(10, 10)).to.be.false; + expect([...selector.points_inside([2, 10], [2, 10])]).to.deep.equals([1, 0]) + expect([...selector.indices_inside([2, 10], [2, 10])]).to.deep.equals([0]) + selector.mouseMove(5, 10); path_data = selector.getData(10, 10) expect(path_data['pixel']['begin']).to.deep.equals([0, 0])