diff --git a/__tests__/src/lib/CanvasGroupings.test.js b/__tests__/src/lib/CanvasGroupings.test.js index 1f222d299a75fe337e4315ac703eca70461ba370..2732be7b629a0af002c1a7535e6a2cc1b775539a 100644 --- a/__tests__/src/lib/CanvasGroupings.test.js +++ b/__tests__/src/lib/CanvasGroupings.test.js @@ -35,6 +35,16 @@ describe('CanvasGroupings', () => { expect(subject.groupings()[1]).toEqual([1, 2]); }); }); + describe('scroll', () => { + let subject; + beforeEach(() => { + subject = new CanvasGroupings([0, 1, 2, 3], 'scroll'); + }); + it('creates an array of all the canvases', () => { + expect(subject.groupings().length).toEqual(1); + expect(subject.groupings()[0]).toEqual([0, 1, 2, 3]); + }); + }); }); describe('getCanvases', () => { describe('single', () => { @@ -58,5 +68,11 @@ describe('CanvasGroupings', () => { expect(subject.getCanvases(2)).toEqual([2]); }); }); + describe('scroll', () => { + it('selects by index', () => { + const subject = new CanvasGroupings([0, 1, 2, 3], 'scroll'); + expect(subject.getCanvases(0)).toEqual([0, 1, 2, 3]); + }); + }); }); }); diff --git a/__tests__/src/lib/CanvasWorld.test.js b/__tests__/src/lib/CanvasWorld.test.js index f30d00ed0184f123f07f70494df83a98afae7768..d010df137176bd2cd6aaa26e7988842ad1f08179 100644 --- a/__tests__/src/lib/CanvasWorld.test.js +++ b/__tests__/src/lib/CanvasWorld.test.js @@ -29,6 +29,14 @@ describe('CanvasWorld', () => { expect(new CanvasWorld(canvasSubset, null, 'right-to-left').contentResourceToWorldCoordinates({ id: 'https://stacks.stanford.edu/image/iiif/rz176rt6531%2FPC0170_s3_Tree_Calendar_20081101_152516_0410/full/full/0/default.jpg' })) .toEqual([0, 0, 2848, 4288]); }); + it('supports TTB orientations', () => { + expect(new CanvasWorld(canvasSubset, null, 'top-to-bottom').contentResourceToWorldCoordinates({ id: 'https://stacks.stanford.edu/image/iiif/rz176rt6531%2FPC0170_s3_Tree_Calendar_20081101_152516_0410/full/full/0/default.jpg' })) + .toEqual([0, 1936, 2848, 4288]); + }); + it('supports BTT orientations', () => { + expect(new CanvasWorld(canvasSubset, null, 'bottom-to-top').contentResourceToWorldCoordinates({ id: 'https://stacks.stanford.edu/image/iiif/rz176rt6531%2FPC0170_s3_Tree_Calendar_20081101_152516_0410/full/full/0/default.jpg' })) + .toEqual([0, 0, 2848, 4288]); + }); it('when placed by a fragment contains the offset', () => { const subject = new CanvasWorld( [Utils.parseManifest(fragmentFixture).getSequences()[0].getCanvases()[0]], diff --git a/src/lib/CanvasGroupings.js b/src/lib/CanvasGroupings.js index ab348f30740da53a7985fb45b9873373b3713a82..690d72a1ec35bdfae2722b1bc6ded86a7b9e2e05 100644 --- a/src/lib/CanvasGroupings.js +++ b/src/lib/CanvasGroupings.js @@ -29,6 +29,9 @@ export default class CanvasGroupings { if (this._groupings) { // eslint-disable-line no-underscore-dangle return this._groupings; // eslint-disable-line no-underscore-dangle } + if (this.viewType === 'continuous') { + return [this.canvases]; + } if (this.viewType !== 'book') { return this.canvases.map(canvas => [canvas]); } diff --git a/src/lib/CanvasWorld.js b/src/lib/CanvasWorld.js index 535f9fac351fdf387eae6e03d0fd079f4df845a4..8b8356254a484f6f35f3ee3a117ba8097e65a6d4 100644 --- a/src/lib/CanvasWorld.js +++ b/src/lib/CanvasWorld.js @@ -13,6 +13,7 @@ export default class CanvasWorld { this.canvases = canvases.map(c => new MiradorCanvas(c)); this.layers = layers; this.viewingDirection = viewingDirection; + this._canvasDimensions = null; // eslint-disable-line no-underscore-dangle } /** */ @@ -20,65 +21,113 @@ export default class CanvasWorld { return this.canvases.map(canvas => canvas.id); } + /** */ + get canvasDimensions() { + if (this._canvasDimensions) { // eslint-disable-line no-underscore-dangle + return this._canvasDimensions; // eslint-disable-line no-underscore-dangle + } + + const [dirX, dirY] = this.canvasDirection; + const scale = dirY === 0 + ? Math.min(...this.canvases.map(c => c.getHeight())) + : Math.min(...this.canvases.map(c => c.getWidth())); + let incX = 0; + let incY = 0; + + const canvasDims = this.canvases.reduce((acc, canvas) => { + let canvasHeight; + let canvasWidth; + + if (dirY === 0) { + // constant height + canvasHeight = scale; + canvasWidth = Math.floor(scale * canvas.aspectRatio); + } else { + // constant width + canvasWidth = scale; + canvasHeight = Math.floor(scale * (1 / canvas.aspectRatio)); + } + acc.push({ + canvas, + height: canvasHeight, + width: canvasWidth, + x: incX, + y: incY, + }); + + incX += dirX * canvasWidth; + incY += dirY * canvasHeight; + return acc; + }, []); + + const worldHeight = dirY === 0 ? scale : Math.abs(incY); + const worldWidth = dirX === 0 ? scale : Math.abs(incX); + + this._canvasDimensions = canvasDims // eslint-disable-line no-underscore-dangle + .reduce((acc, dims) => { + acc.push({ + ...dims, + x: dirX === -1 ? dims.x + worldWidth - dims.width : dims.x, + y: dirY === -1 ? dims.y + worldHeight - dims.height : dims.y, + }); + + return acc; + }, []); + + return this._canvasDimensions; // eslint-disable-line no-underscore-dangle + } + /** * contentResourceToWorldCoordinates - calculates the contentResource coordinates * respective to the world. */ contentResourceToWorldCoordinates(contentResource) { - const wholeBounds = this.worldBounds(); const miradorCanvasIndex = this.canvases.findIndex(c => ( c.imageResources.find(r => r.id === contentResource.id) )); const canvas = this.canvases[miradorCanvasIndex]; - const scaledWidth = Math.floor(wholeBounds[3] * canvas.aspectRatio); - let x = 0; - if (miradorCanvasIndex === this.secondCanvasIndex) { - x = wholeBounds[2] - scaledWidth; - } + const [x, y, w, h] = this.canvasToWorldCoordinates(canvas.id); + const fragmentOffset = canvas.onFragment(contentResource.id); if (fragmentOffset) { return [ x + fragmentOffset[0], - 0 + fragmentOffset[1], + y + fragmentOffset[1], fragmentOffset[2], fragmentOffset[3], ]; } return [ x, - 0, - scaledWidth, - wholeBounds[3], + y, + w, + h, ]; } /** */ canvasToWorldCoordinates(canvasId) { - const wholeBounds = this.worldBounds(); - const miradorCanvasIndex = this.canvases.findIndex(c => (c.id === canvasId)); - const { aspectRatio } = this.canvases[miradorCanvasIndex]; - const scaledWidth = Math.floor(wholeBounds[3] * aspectRatio); - let x = 0; - if (miradorCanvasIndex === this.secondCanvasIndex) { - x = wholeBounds[2] - scaledWidth; - } + const canvasDimensions = this.canvasDimensions.find(c => c.canvas.id === canvasId); + return [ - x, - 0, - scaledWidth, - wholeBounds[3], + canvasDimensions.x, + canvasDimensions.y, + canvasDimensions.width, + canvasDimensions.height, ]; } - /** - * secondCanvasIndex - index of the second canvas used for determining which - * is first - */ - get secondCanvasIndex() { - return this.viewingDirection === 'right-to-left' ? 0 : 1; + /** */ + get canvasDirection() { + switch (this.viewingDirection) { + case 'left-to-right': return [1, 0]; + case 'right-to-left': return [-1, 0]; + case 'top-to-bottom': return [0, 1]; + case 'bottom-to-top': return [0, -1]; + default: return [0, 1]; + } } - /** Get the IIIF content resource for an image */ contentResource(infoResponseId) { const miradorCanvas = this.canvases.find(c => c.imageServiceIds.some(id => ( @@ -149,26 +198,14 @@ export default class CanvasWorld { * lined up horizontally starting from left to right. */ worldBounds() { - const heights = []; - const dimensions = []; - this.canvases.forEach((canvas) => { - heights.push(canvas.getHeight()); - dimensions.push({ - height: canvas.getHeight(), - width: canvas.getWidth(), - }); - }); - const minHeight = Math.min(...heights); - let scaledWidth = 0; - dimensions.forEach((dim) => { - const aspectRatio = dim.width / dim.height; - scaledWidth += Math.floor(minHeight * aspectRatio); - }); + const worldWidth = Math.max(...this.canvasDimensions.map(c => c.x + c.width)); + const worldHeight = Math.max(...this.canvasDimensions.map(c => c.y + c.height)); + return [ 0, 0, - scaledWidth, - minHeight, + worldWidth, + worldHeight, ]; } } diff --git a/src/state/selectors/windows.js b/src/state/selectors/windows.js index 4ffbe1422d892edc4cf24a0937c86548f9c5d5cf..245fb617c0463b068d1e55715daf668419434bfd 100644 --- a/src/state/selectors/windows.js +++ b/src/state/selectors/windows.js @@ -88,6 +88,7 @@ export const getWindowViewType = createSelector( ], (window, manifestViewingHint, manifestBehaviors, defaultView) => { const lookup = { + continuous: 'continuous', individuals: 'single', paged: 'book', };