mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-12-01 09:36:08 +03:00
Merge pull request #1772 from zero41120/feature/1540-cropping-with-multiple-polygons
Feature/1540 cropping with multiple polygons
This commit is contained in:
commit
17cabdb923
@ -166,6 +166,41 @@ $.Drawer.prototype = {
|
|||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 vpPoint = this.viewport.pixelFromPointNoRotate(point, true);
|
||||||
|
return new $.Point(
|
||||||
|
vpPoint.x * $.pixelDensityRatio,
|
||||||
|
vpPoint.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.
|
* Set the opacity of the drawer.
|
||||||
* @param {Number} opacity
|
* @param {Number} opacity
|
||||||
|
@ -674,6 +674,58 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
this._setScale(height / this.normHeight, immediately);
|
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 point object in image coordinates.
|
||||||
|
* Example format: [
|
||||||
|
* [{x: 197, y:172}, {x: 226, y:172}, {x: 226, y:198}, {x: 197, y:198}], // First polygon
|
||||||
|
* [{x: 328, y:200}, {x: 330, y:199}, {x: 332, y:201}, {x: 329, y:202}] // Second polygon
|
||||||
|
* [{x: 321, y:201}, {x: 356, y:205}, {x: 341, y:250}] // Third polygon
|
||||||
|
* ]
|
||||||
|
*/
|
||||||
|
setCroppingPolygons: function( polygons ) {
|
||||||
|
|
||||||
|
var isXYObject = function(obj) {
|
||||||
|
return obj instanceof $.Point || (typeof obj.x === 'number' && typeof obj.y === 'number');
|
||||||
|
};
|
||||||
|
|
||||||
|
var objectToSimpleXYObject = function(objs) {
|
||||||
|
return objs.map(function(obj) {
|
||||||
|
try {
|
||||||
|
if (isXYObject(obj)) {
|
||||||
|
return { x: obj.x, y: obj.y };
|
||||||
|
} 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 objectToSimpleXYObject(polygon);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
$.console.error('[TiledImage.setCroppingPolygons] Cropping polygon format not supported');
|
||||||
|
$.console.error(e);
|
||||||
|
this._croppingPolygons = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Positions and scales the TiledImage to fit in the specified bounds.
|
||||||
* Note: this method fires OpenSeadragon.TiledImage.event:bounds-change
|
* Note: this method fires OpenSeadragon.TiledImage.event:bounds-change
|
||||||
@ -1932,6 +1984,28 @@ function drawTiles( tiledImage, lastDrawn ) {
|
|||||||
usedClip = true;
|
usedClip = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tiledImage._croppingPolygons) {
|
||||||
|
tiledImage._drawer.saveContext(useSketch);
|
||||||
|
try {
|
||||||
|
var polygons = tiledImage._croppingPolygons.map(function (polygon) {
|
||||||
|
return polygon.map(function (coord) {
|
||||||
|
var point = tiledImage
|
||||||
|
.imageToViewportCoordinates(coord.x, coord.y, 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 ) {
|
if ( tiledImage.placeholderFillStyle && tiledImage._hasOpaqueTile === false ) {
|
||||||
var placeholderRect = tiledImage._drawer.viewportToDrawerRectangle(tiledImage.getBounds(true));
|
var placeholderRect = tiledImage._drawer.viewportToDrawerRectangle(tiledImage.getBounds(true));
|
||||||
if (sketchScale) {
|
if (sketchScale) {
|
||||||
|
165
test/demo/cropping-polygons.html
Normal file
165
test/demo/cropping-polygons.html
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>OpenSeadragon Cropping PolygonList Demo</title>
|
||||||
|
<script type="text/javascript" src='../../build/openseadragon/openseadragon.js'></script>
|
||||||
|
<script type="text/javascript" src='../lib/jquery-1.9.1.min.js'></script>
|
||||||
|
<style type="text/css">
|
||||||
|
|
||||||
|
.openseadragon1 {
|
||||||
|
width: 800px;
|
||||||
|
height: 600px;
|
||||||
|
background: lightgreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 215px;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-with-title {
|
||||||
|
padding-top: 1em;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.buttons {
|
||||||
|
width: 215px;
|
||||||
|
}
|
||||||
|
|
||||||
|
*:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h3>
|
||||||
|
Simple demo page to show cropping with polygonList in a OpenSeadragon viewer.
|
||||||
|
</h3>
|
||||||
|
<div id="contentDiv" class="openseadragon1"></div>
|
||||||
|
<span>Click on Viewer to save polygon points</span>
|
||||||
|
<div>
|
||||||
|
<button id='resetBtn'>Reset</button>
|
||||||
|
<button id='exampleBtn'>Load Example</button>
|
||||||
|
</div>
|
||||||
|
<div class='box-with-title'>
|
||||||
|
<div class="buttons">
|
||||||
|
<button id="addPointBtn">Add Points as Polygon</button>
|
||||||
|
<button onclick="emptyElement('polygonPointEl')">Clear</button>
|
||||||
|
</div>
|
||||||
|
<textarea id="polygonPointEl"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class='box-with-title'>
|
||||||
|
<div class="buttons">
|
||||||
|
<button id="cropBtn">Crop With Polygon</button>
|
||||||
|
<button onclick="emptyElement('previewEl')">Clear</button>
|
||||||
|
</div>
|
||||||
|
<textarea id='previewEl'></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Setup Viewer -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var viewer = OpenSeadragon({
|
||||||
|
// debugMode: true,
|
||||||
|
id: "contentDiv",
|
||||||
|
prefixUrl: "../../build/openseadragon/images/",
|
||||||
|
tileSources: "../data/testpattern.dzi",
|
||||||
|
showNavigator: false,
|
||||||
|
gestureSettingsMouse: {
|
||||||
|
clickToZoom: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Global Variables
|
||||||
|
var previewEl = document.getElementById('previewEl');
|
||||||
|
var polygonPointEl = document.getElementById('polygonPointEl');
|
||||||
|
|
||||||
|
var examples = [
|
||||||
|
[{x: 480, y: 300},{x: 300, y: 420},{x: 600, y: 420}], // Triangle
|
||||||
|
[{x: 300, y: 550},{x: 300, y: 750},{x: 600, y: 750},{x: 600, y: 550}] // Rectangle
|
||||||
|
];
|
||||||
|
|
||||||
|
// Load default examples
|
||||||
|
function loadExample(){
|
||||||
|
previewEl.value = JSON.stringify(examples);
|
||||||
|
}
|
||||||
|
loadExample();
|
||||||
|
|
||||||
|
// Set a given element's value to empty string
|
||||||
|
function emptyElement(elementId) {
|
||||||
|
document.getElementById(elementId).value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON parse a given object, then insert object assuming parsed value is array.
|
||||||
|
function insertObjectToElement(object, element) {
|
||||||
|
var parsed = []; // Default to empty array
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(element.value);
|
||||||
|
} catch(error) { }
|
||||||
|
parsed.push(object);
|
||||||
|
element.value = JSON.stringify(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add click handler to clicked point tracker
|
||||||
|
viewer.addHandler('canvas-click', function(event) {
|
||||||
|
var viewportPoint = viewer.viewport.pointFromPixel(event.position);
|
||||||
|
var p = viewer.viewport.viewportToImageCoordinates(viewportPoint);
|
||||||
|
p.x = Math.round((p.x + Number.EPSILON) * 100) / 100
|
||||||
|
p.y = Math.round((p.y + Number.EPSILON) * 100) / 100
|
||||||
|
insertObjectToElement({x:p.x, y:p.y}, polygonPointEl);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Evaluate give element in JavaScript variable, default to empty array.
|
||||||
|
function readElementValueDefaultToEmptyArray(elementId) {
|
||||||
|
try {
|
||||||
|
var val = document.getElementById(elementId).value;
|
||||||
|
if (val === '') { return []; }
|
||||||
|
return eval(val); // If using JSON.parse, user must put quotes [{"x": 123, "y":12}]
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert value from given element into preview element
|
||||||
|
function insertValueFromElementToPreviewElement(element) {
|
||||||
|
try {
|
||||||
|
if (element.value === '') { return; }
|
||||||
|
var polygon = eval(element.value);
|
||||||
|
var polygonList = readElementValueDefaultToEmptyArray('previewEl');
|
||||||
|
polygonList.push(polygon);
|
||||||
|
element.value = '';
|
||||||
|
previewEl.value = JSON.stringify(polygonList);
|
||||||
|
} catch(error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add clicked points to polygon tracker variable as point objects
|
||||||
|
document.getElementById('addPointBtn').onclick = function(){
|
||||||
|
insertValueFromElementToPreviewElement(polygonPointEl);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Crop image with value in the preview element
|
||||||
|
document.getElementById('cropBtn').onclick = function(){
|
||||||
|
var polygonList = eval(previewEl.value);
|
||||||
|
var tiledImage = viewer.world.getItemAt(0);
|
||||||
|
tiledImage.setCroppingPolygons(polygonList);
|
||||||
|
viewer.forceRedraw();
|
||||||
|
emptyElement('previewEl');
|
||||||
|
};
|
||||||
|
|
||||||
|
document.getElementById('resetBtn').onclick = function(){
|
||||||
|
emptyElement('polygonPointEl');
|
||||||
|
emptyElement('previewEl');
|
||||||
|
var tiledImage = viewer.world.getItemAt(0);
|
||||||
|
tiledImage.resetCroppingPolygons();
|
||||||
|
viewer.forceRedraw();
|
||||||
|
};
|
||||||
|
|
||||||
|
document.getElementById('exampleBtn').onclick = loadExample;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user