From 2d971af4458c79c9b69a3411a1623a1f75b673a4 Mon Sep 17 00:00:00 2001 From: zero41120 Date: Thu, 6 Feb 2020 13:46:14 -0800 Subject: [PATCH 01/12] 1540 Add feature to crop tiledImage with multiple polygons --- src/drawer.js | 32 ++++++++ src/tiledimage.js | 43 +++++++++++ test/demo/cropping-polygons.html | 128 +++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 test/demo/cropping-polygons.html diff --git a/src/drawer.js b/src/drawer.js index ff658aaf..4cd7ee4b 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -166,6 +166,38 @@ $.Drawer.prototype = { return this; }, + /** + * This function gets the top left point from the viewport using the pixel point + * @param {OpenSeadragon.Point} point - the pixel points to convert + */ + viewportCoordToDrawerCoord: function(point) { + var topLeft = this.viewport.pixelFromPointNoRotate(point, true); + return new $.Point( + topLeft.x * $.pixelDensityRatio, + topLeft.y * $.pixelDensityRatio + ); + }, + + /** + * This function will create multiple polygon paths on the drawing context by provided polygons, + * then clip the context to the paths. + * @param {(OpenSeadragon.Point[])[]} polygons - an array of polygons. A polygon is an array of OpenSeadragon.Point + * @param {Boolean} useSketch - Whether to use the sketch canvas or not. + */ + clipWithPolygons: function (polygons, useSketch) { + if (!this.useCanvas) { + return; + } + var context = this._getContext(useSketch); + context.beginPath(); + polygons.forEach(function (polygon) { + polygon.forEach(function (coord, i) { + context[i == 0 ? 'moveTo' : 'lineTo'](coord.x, coord.y); + }); + }); + context.clip(); + }, + /** * Set the opacity of the drawer. * @param {Number} opacity diff --git a/src/tiledimage.js b/src/tiledimage.js index 54cc84fe..2e829cd5 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -674,6 +674,27 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag this._setScale(height / this.normHeight, immediately); }, + /** + * Sets an array of polygons to crop the TiledImage during draw tiles. + * The render function will use the default non-zero winding rule. + * @param Polygons represented in an array of pair array in pixels. + * Example format: [ + * [[197,172],[226,172],[226,198],[197,198]], // First polygon + * [[328,200],[330,199],[332,201],[329,202]] // Second polygon + * ] + */ + setCroppingPolygons: function( polygons ) { + this._croppingPolygons = polygons; + }, + + /** + * Resets the cropping polygons, thus next render will remove all cropping + * polygon effects. + */ + resetCroppingPolygons: function() { + this._croppingPolygons = null; + }, + /** * Positions and scales the TiledImage to fit in the specified bounds. * Note: this method fires OpenSeadragon.TiledImage.event:bounds-change @@ -1932,6 +1953,28 @@ function drawTiles( tiledImage, lastDrawn ) { usedClip = true; } + if (tiledImage._croppingPolygons) { + tiledImage._drawer.saveContext(useSketch); + try { + var polygons = tiledImage._croppingPolygons.map(function (polygon) { + return polygon.map(function (pointPair) { + var point = tiledImage + .imageToViewportCoordinates(pointPair[0], pointPair[1], true) + .rotate(-tiledImage.getRotation(true), tiledImage._getRotationPoint(true)); + var clipPoint = tiledImage._drawer.viewportCoordToDrawerCoord(point); + if (sketchScale) { + clipPoint = clipPoint.times(sketchScale); + } + return clipPoint; + }); + }); + tiledImage._drawer.clipWithPolygons(polygons, useSketch); + } catch (e) { + $.console.error(e); + } + usedClip = true; + } + if ( tiledImage.placeholderFillStyle && tiledImage._hasOpaqueTile === false ) { var placeholderRect = tiledImage._drawer.viewportToDrawerRectangle(tiledImage.getBounds(true)); if (sketchScale) { diff --git a/test/demo/cropping-polygons.html b/test/demo/cropping-polygons.html new file mode 100644 index 00000000..0e523f4d --- /dev/null +++ b/test/demo/cropping-polygons.html @@ -0,0 +1,128 @@ + + + + OpenSeadragon Cropping PolygonList Demo + + + + + +

+ Simple demo page to show cropping with polygonList in a OpenSeadragon viewer. +

+
+ Click on Viewer to save polygon points +
+ + +
+
+ + +
+
+ + +
+ + + + + + + + From 94183f7f18c8186a584085183c6a40f78bda74c4 Mon Sep 17 00:00:00 2001 From: zero41120 Date: Thu, 6 Feb 2020 13:50:15 -0800 Subject: [PATCH 02/12] 1540 Change innerHTML to value to sync user input --- test/demo/cropping-polygons.html | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/demo/cropping-polygons.html b/test/demo/cropping-polygons.html index 0e523f4d..071e8405 100644 --- a/test/demo/cropping-polygons.html +++ b/test/demo/cropping-polygons.html @@ -79,7 +79,7 @@ // Load default examples function loadExample(){ polygonList = JSON.parse(JSON.stringify(examples)); - previewEl.innerHTML = JSON.stringify(polygonList); + previewEl.value = JSON.stringify(polygonList); } loadExample(); @@ -91,7 +91,7 @@ p.x = Math.round((p.x + Number.EPSILON) * 100) / 100 p.y = Math.round((p.y + Number.EPSILON) * 100) / 100 polygon.push([p.x, p.y]); - polygonEl.innerHTML += '['+p.x+','+ p.y+']'; + polygonEl.value += '['+p.x+','+ p.y+']'; }); // Add clicked points to polygon tracker variable @@ -99,13 +99,13 @@ if (polygon.length == 0) { return; } polygonList.push(polygon); polygon = []; - polygonEl.innerHTML = ''; - previewEl.innerHTML = JSON.stringify(polygonList); + polygonEl.value = ''; + previewEl.value = JSON.stringify(polygonList); }; document.getElementById('cropBtn').onclick = function(){ - polygonList = JSON.parse(previewEl.innerHTML); - polygonEl.innerHTML = ''; + polygonList = JSON.parse(previewEl.value); + polygonEl.value = ''; var tiledImage = viewer.world.getItemAt(0); tiledImage.setCroppingPolygons(polygonList); viewer.forceRedraw(); @@ -114,8 +114,8 @@ document.getElementById('resetBtn').onclick = function(){ polygonList = [] polygon = [] - polygonEl.innerHTML = ''; - previewEl.innerHTML = ''; + polygonEl.value = ''; + previewEl.value = ''; var tiledImage = viewer.world.getItemAt(0); tiledImage.resetCroppingPolygons(); viewer.forceRedraw(); From dadef91ce0c2a01079cd3076e2cff2cc2c6bc69d Mon Sep 17 00:00:00 2001 From: Yansong Hong Date: Fri, 7 Feb 2020 13:15:03 -0800 Subject: [PATCH 03/12] 1540 Address reviewer comment to better document viewportCoordToDrawerCoord function --- src/drawer.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 4cd7ee4b..acd0eb46 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -167,14 +167,17 @@ $.Drawer.prototype = { }, /** - * This function gets the top left point from the viewport using the pixel point - * @param {OpenSeadragon.Point} point - the pixel points to convert + * This function converts the given point from to the drawer coordinate by + * multiplying it with the pixel density. + * This function does not take rotation into account, thus assuming provided + * point is at 0 degree. + * @param {OpenSeadragon.Point} point - the pixel point to convert */ viewportCoordToDrawerCoord: function(point) { - var topLeft = this.viewport.pixelFromPointNoRotate(point, true); + var vpPoint = this.viewport.pixelFromPointNoRotate(point, true); return new $.Point( - topLeft.x * $.pixelDensityRatio, - topLeft.y * $.pixelDensityRatio + vpPoint.x * $.pixelDensityRatio, + vpPoint.y * $.pixelDensityRatio ); }, From b4bf21cda8f54142a43c18521a9475caceb33c30 Mon Sep 17 00:00:00 2001 From: Yansong Hong Date: Fri, 7 Feb 2020 13:16:09 -0800 Subject: [PATCH 04/12] 1540 Use strict equality instead of loose equality --- src/drawer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drawer.js b/src/drawer.js index acd0eb46..18a63ece 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -195,7 +195,7 @@ $.Drawer.prototype = { context.beginPath(); polygons.forEach(function (polygon) { polygon.forEach(function (coord, i) { - context[i == 0 ? 'moveTo' : 'lineTo'](coord.x, coord.y); + context[i === 0 ? 'moveTo' : 'lineTo'](coord.x, coord.y); }); }); context.clip(); From 7193f5b445efd9ace67cb45be93be622d74b4281 Mon Sep 17 00:00:00 2001 From: Yansong Hong Date: Fri, 7 Feb 2020 16:03:40 -0800 Subject: [PATCH 05/12] 1540 Add support for xy point object and mixed array --- src/tiledimage.js | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 2e829cd5..e0e5b30c 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -677,14 +677,54 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag /** * Sets an array of polygons to crop the TiledImage during draw tiles. * The render function will use the default non-zero winding rule. - * @param Polygons represented in an array of pair array in pixels. + * @param Polygons represented in an array of pair array or point object in pixels. * Example format: [ * [[197,172],[226,172],[226,198],[197,198]], // First polygon * [[328,200],[330,199],[332,201],[329,202]] // Second polygon + * [{x: 321, y:201}, {x: 356, y:205}, {x: 341, y:250}] // Third polygon * ] + * Argument may mix array and point objects. + * Point objects will be convert to array. */ setCroppingPolygons: function( polygons ) { - this._croppingPolygons = polygons; + + var isXYObject = function(obj) { + return obj instanceof $.Point || (typeof obj.x === 'number' && typeof obj.y === 'number'); + }; + var isArray = function(obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; + }; + var isXYPair = function(obj) { + return obj && isArray(obj) && obj.length === 2; + }; + var convertXYObjectsToArrayIfNeeded = function(objs) { + return objs.map(function(obj) { + try { + if (isXYObject(obj)) { + return [obj.x, obj.y]; + } else if (isXYPair(obj)) { + return obj; + } else { + throw new Error(); + } + } catch(e) { + throw new Error('A Provided cropping polygon point is not supported'); + } + }); + }; + + try { + if (!isArray(polygons)) { + throw new Error('Provided cropping polygon is not an array'); + } + this._croppingPolygons = polygons.map(function(polygon){ + return convertXYObjectsToArrayIfNeeded(polygon); + }); + } catch (e) { + $.console.error('[TiledImage.setCroppingPolygons] Cropping polygon format not supported'); + $.console.error(e); + this._croppingPolygons = null; + } }, /** From 956c830f3e0d3e446b42ead85124a17fd09d5a7e Mon Sep 17 00:00:00 2001 From: Yansong Hong Date: Fri, 7 Feb 2020 16:04:16 -0800 Subject: [PATCH 06/12] 1540 Add point support on the demo page --- test/demo/cropping-polygons.html | 105 +++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 27 deletions(-) diff --git a/test/demo/cropping-polygons.html b/test/demo/cropping-polygons.html index 071e8405..368f6b53 100644 --- a/test/demo/cropping-polygons.html +++ b/test/demo/cropping-polygons.html @@ -13,7 +13,7 @@ } textarea { - width: 200px; + width: 215px; height: 200px; } @@ -22,8 +22,8 @@ display: inline-block; text-align: center; } - .box-with-title button{ - display: block; + .buttons { + width: 215px; } *:focus { @@ -43,11 +43,24 @@
- - +
+ + +
+
- +
+ + +
+ +
+
+
+ + +
@@ -68,54 +81,92 @@