med-viewer-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +253 -0
  2. package/dist/adapters/vue/MedViewer.d.ts +17 -0
  3. package/dist/adapters/vue/index.d.ts +2 -0
  4. package/dist/core/AnnoAnnotator.d.ts +15 -0
  5. package/dist/core/BaseAnnotator.d.ts +33 -0
  6. package/dist/core/ColorAdjustPlugin.d.ts +29 -0
  7. package/dist/core/Coords.d.ts +6 -0
  8. package/dist/core/Engine.d.ts +57 -0
  9. package/dist/core/KonvaAnnotator.d.ts +36 -0
  10. package/dist/core/Scalebar.d.ts +42 -0
  11. package/dist/core/SelectionPlugin.d.ts +102 -0
  12. package/dist/core/Toolbar.d.ts +32 -0
  13. package/dist/med-viewer-sdk.d.ts +6 -0
  14. package/dist/med-viewer-sdk.mjs +14248 -0
  15. package/dist/med-viewer-sdk.umd.js +2 -0
  16. package/dist/style.css +1 -0
  17. package/package.json +34 -0
  18. package/src/adapters/vue/MedViewer.ts +37 -0
  19. package/src/adapters/vue/index.ts +4 -0
  20. package/src/assets/icons/button_grouphover.png +0 -0
  21. package/src/assets/icons/button_hover.png +0 -0
  22. package/src/assets/icons/button_pressed.png +0 -0
  23. package/src/assets/icons/button_rest.png +0 -0
  24. package/src/assets/icons/flip_grouphover.png +0 -0
  25. package/src/assets/icons/flip_hover.png +0 -0
  26. package/src/assets/icons/flip_pressed.png +0 -0
  27. package/src/assets/icons/flip_rest.png +0 -0
  28. package/src/assets/icons/fullpage_grouphover.png +0 -0
  29. package/src/assets/icons/fullpage_hover.png +0 -0
  30. package/src/assets/icons/fullpage_pressed.png +0 -0
  31. package/src/assets/icons/fullpage_rest.png +0 -0
  32. package/src/assets/icons/home_grouphover.png +0 -0
  33. package/src/assets/icons/home_hover.png +0 -0
  34. package/src/assets/icons/home_pressed.png +0 -0
  35. package/src/assets/icons/home_rest.png +0 -0
  36. package/src/assets/icons/next_grouphover.png +0 -0
  37. package/src/assets/icons/next_hover.png +0 -0
  38. package/src/assets/icons/next_pressed.png +0 -0
  39. package/src/assets/icons/next_rest.png +0 -0
  40. package/src/assets/icons/previous_grouphover.png +0 -0
  41. package/src/assets/icons/previous_hover.png +0 -0
  42. package/src/assets/icons/previous_pressed.png +0 -0
  43. package/src/assets/icons/previous_rest.png +0 -0
  44. package/src/assets/icons/rotateleft_grouphover.png +0 -0
  45. package/src/assets/icons/rotateleft_hover.png +0 -0
  46. package/src/assets/icons/rotateleft_pressed.png +0 -0
  47. package/src/assets/icons/rotateleft_rest.png +0 -0
  48. package/src/assets/icons/rotateright_grouphover.png +0 -0
  49. package/src/assets/icons/rotateright_hover.png +0 -0
  50. package/src/assets/icons/rotateright_pressed.png +0 -0
  51. package/src/assets/icons/rotateright_rest.png +0 -0
  52. package/src/assets/icons/selection_cancel_grouphover.png +0 -0
  53. package/src/assets/icons/selection_cancel_hover.png +0 -0
  54. package/src/assets/icons/selection_cancel_pressed.png +0 -0
  55. package/src/assets/icons/selection_cancel_rest.png +0 -0
  56. package/src/assets/icons/selection_confirm_grouphover.png +0 -0
  57. package/src/assets/icons/selection_confirm_hover.png +0 -0
  58. package/src/assets/icons/selection_confirm_pressed.png +0 -0
  59. package/src/assets/icons/selection_confirm_rest.png +0 -0
  60. package/src/assets/icons/selection_grouphover.png +0 -0
  61. package/src/assets/icons/selection_hover.png +0 -0
  62. package/src/assets/icons/selection_pressed.png +0 -0
  63. package/src/assets/icons/selection_rest.png +0 -0
  64. package/src/assets/icons/tool_anno.png +0 -0
  65. package/src/assets/icons/tool_selection.png +0 -0
  66. package/src/assets/icons/zoomin_grouphover.png +0 -0
  67. package/src/assets/icons/zoomin_hover.png +0 -0
  68. package/src/assets/icons/zoomin_pressed.png +0 -0
  69. package/src/assets/icons/zoomin_rest.png +0 -0
  70. package/src/assets/icons/zoomout_grouphover.png +0 -0
  71. package/src/assets/icons/zoomout_hover.png +0 -0
  72. package/src/assets/icons/zoomout_pressed.png +0 -0
  73. package/src/assets/icons/zoomout_rest.png +0 -0
  74. package/src/core/AnnoAnnotator.ts +102 -0
  75. package/src/core/BaseAnnotator.ts +43 -0
  76. package/src/core/ColorAdjustPlugin.ts +256 -0
  77. package/src/core/Coords.ts +9 -0
  78. package/src/core/Engine.ts +246 -0
  79. package/src/core/KonvaAnnotator.ts +185 -0
  80. package/src/core/Scalebar.ts +87 -0
  81. package/src/core/SelectionPlugin.ts +252 -0
  82. package/src/core/Toolbar.ts +370 -0
  83. package/src/index.ts +21 -0
  84. package/src/plugins/ShapeLabelsFormatter.js +435 -0
  85. package/src/plugins/openseadragon-scalebar.js +592 -0
  86. package/src/plugins/openseadragon-selection.js +657 -0
  87. package/src/types/type.d.ts +9 -0
@@ -0,0 +1,592 @@
1
+ import OpenSeadragon from "openseadragon";
2
+
3
+ /*
4
+ * This software was developed at the National Institute of Standards and
5
+ * Technology by employees of the Federal Government in the course of
6
+ * their official duties. Pursuant to title 17 Section 105 of the United
7
+ * States Code this software is not subject to copyright protection and is
8
+ * in the public domain. This software is an experimental system. NIST assumes
9
+ * no responsibility whatsoever for its use by other parties, and makes no
10
+ * guarantees, expressed or implied, about its quality, reliability, or
11
+ * any other characteristic. We would appreciate acknowledgement if the
12
+ * software is used.
13
+ */
14
+
15
+ /**
16
+ *
17
+ * @author Antoine Vandecreme <antoine.vandecreme@nist.gov>
18
+ */
19
+ (function ($) {
20
+ if (!$.version || $.version.major < 2) {
21
+ throw new Error(
22
+ "This version of OpenSeadragonScalebar requires " +
23
+ "OpenSeadragon version 2.0.0+"
24
+ );
25
+ }
26
+
27
+ $.Viewer.prototype.scalebar = function (options) {
28
+ if (!this.scalebarInstance) {
29
+ options = options || {};
30
+ options.viewer = this;
31
+ this.scalebarInstance = new $.Scalebar(options);
32
+ } else {
33
+ this.scalebarInstance.refresh(options);
34
+ }
35
+ };
36
+
37
+ $.ScalebarType = {
38
+ NONE: 0,
39
+ MICROSCOPY: 1,
40
+ MAP: 2,
41
+ };
42
+
43
+ $.ScalebarLocation = {
44
+ NONE: 0,
45
+ TOP_LEFT: 1,
46
+ TOP_RIGHT: 2,
47
+ BOTTOM_RIGHT: 3,
48
+ BOTTOM_LEFT: 4,
49
+ };
50
+
51
+ /**
52
+ *
53
+ * @class Scalebar
54
+ * @param {Object} options
55
+ * @param {OpenSeadragon.Viewer} options.viewer The viewer to attach this
56
+ * Scalebar to.
57
+ * @param {OpenSeadragon.ScalebarType} options.type The scale bar type.
58
+ * Default: microscopy
59
+ * @param {Integer} options.pixelsPerMeter The pixels per meter of the
60
+ * zoomable image at the original image size. If null, the scale bar is not
61
+ * displayed. default: null
62
+ * @param {Integer} options.referenceItemIdx Specify the item from
63
+ * viewer.world to which options.pixelsPerMeter is refering.
64
+ * default: 0
65
+ * @param (String} options.minWidth The minimal width of the scale bar as a
66
+ * CSS string (ex: 100px, 1em, 1% etc...) default: 150px
67
+ * @param {OpenSeadragon.ScalebarLocation} options.location The location
68
+ * of the scale bar inside the viewer. default: bottom left
69
+ * @param {Integer} options.xOffset Offset location of the scale bar along x.
70
+ * default: 5
71
+ * @param {Integer} options.yOffset Offset location of the scale bar along y.
72
+ * default: 5
73
+ * @param {Boolean} options.stayInsideImage When set to true, keep the
74
+ * scale bar inside the image when zooming out. default: true
75
+ * @param {String} options.color The color of the scale bar using a color
76
+ * name or the hexadecimal format (ex: black or #000000) default: black
77
+ * @param {String} options.fontColor The font color. default: black
78
+ * @param {String} options.backgroundColor The background color. default: none
79
+ * @param {String} options.fontSize The font size. default: not set
80
+ * @param {String} options.fontFamily The font-family. default: not set
81
+ * @param {String} options.barThickness The thickness of the scale bar in px.
82
+ * default: 2
83
+ * @param {function} options.sizeAndTextRenderer A function which will be
84
+ * called to determine the size of the scale bar and it's text content.
85
+ * The function must have 2 parameters: the PPM at the current zoom level
86
+ * and the minimum size of the scale bar. It must return an object containing
87
+ * 2 attributes: size and text containing the size of the scale bar and the text.
88
+ * default: $.ScalebarSizeAndTextRenderer.METRIC_LENGTH
89
+ */
90
+ $.Scalebar = function (options) {
91
+ options = options || {};
92
+ if (!options.viewer) {
93
+ throw new Error("A viewer must be specified.");
94
+ }
95
+ this.viewer = options.viewer;
96
+
97
+ this.divElt = document.createElement("div");
98
+ this.viewer.container.appendChild(this.divElt);
99
+ this.divElt.style.position = "relative";
100
+ this.divElt.style.margin = "0";
101
+ this.divElt.style.pointerEvents = "none";
102
+
103
+ this.setMinWidth(options.minWidth || "150px");
104
+
105
+ this.setDrawScalebarFunction(options.type || $.ScalebarType.MICROSCOPY);
106
+ this.color = options.color || "black";
107
+ this.fontColor = options.fontColor || "black";
108
+ this.backgroundColor = options.backgroundColor || "none";
109
+ this.fontSize = options.fontSize || "";
110
+ this.fontFamily = options.fontFamily || "";
111
+ this.barThickness = options.barThickness || 2;
112
+ this.pixelsPerMeter = options.pixelsPerMeter || null;
113
+ this.referenceItemIdx = options.referenceItemIdx || 0;
114
+ this.location = options.location || $.ScalebarLocation.BOTTOM_LEFT;
115
+ this.xOffset = options.xOffset || 5;
116
+ this.yOffset = options.yOffset || 5;
117
+ this.stayInsideImage = isDefined(options.stayInsideImage)
118
+ ? options.stayInsideImage
119
+ : true;
120
+ this.sizeAndTextRenderer =
121
+ options.sizeAndTextRenderer ||
122
+ $.ScalebarSizeAndTextRenderer.METRIC_LENGTH;
123
+
124
+ var self = this;
125
+ this.viewer.addHandler("open", function () {
126
+ self.refresh();
127
+ });
128
+ this.viewer.addHandler("animation", function () {
129
+ self.refresh();
130
+ });
131
+ this.viewer.addHandler("resize", function () {
132
+ self.refresh();
133
+ });
134
+ };
135
+
136
+ $.Scalebar.prototype = {
137
+ updateOptions: function (options) {
138
+ if (!options) {
139
+ return;
140
+ }
141
+ if (isDefined(options.type)) {
142
+ this.setDrawScalebarFunction(options.type);
143
+ }
144
+ if (isDefined(options.minWidth)) {
145
+ this.setMinWidth(options.minWidth);
146
+ }
147
+ if (isDefined(options.color)) {
148
+ this.color = options.color;
149
+ }
150
+ if (isDefined(options.fontColor)) {
151
+ this.fontColor = options.fontColor;
152
+ }
153
+ if (isDefined(options.backgroundColor)) {
154
+ this.backgroundColor = options.backgroundColor;
155
+ }
156
+ if (isDefined(options.fontSize)) {
157
+ this.fontSize = options.fontSize;
158
+ }
159
+ if (isDefined(options.fontFamily)) {
160
+ this.fontFamily = options.fontFamily;
161
+ }
162
+ if (isDefined(options.barThickness)) {
163
+ this.barThickness = options.barThickness;
164
+ }
165
+ if (isDefined(options.pixelsPerMeter)) {
166
+ this.pixelsPerMeter = options.pixelsPerMeter;
167
+ }
168
+ if (isDefined(options.referenceItemIdx)) {
169
+ this.referenceItemIdx = options.referenceItemIdx;
170
+ }
171
+ if (isDefined(options.location)) {
172
+ this.location = options.location;
173
+ }
174
+ if (isDefined(options.xOffset)) {
175
+ this.xOffset = options.xOffset;
176
+ }
177
+ if (isDefined(options.yOffset)) {
178
+ this.yOffset = options.yOffset;
179
+ }
180
+ if (isDefined(options.stayInsideImage)) {
181
+ this.stayInsideImage = options.stayInsideImage;
182
+ }
183
+ if (isDefined(options.sizeAndTextRenderer)) {
184
+ this.sizeAndTextRenderer = options.sizeAndTextRenderer;
185
+ }
186
+ },
187
+ setDrawScalebarFunction: function (type) {
188
+ if (!type) {
189
+ this.drawScalebar = null;
190
+ } else if (type === $.ScalebarType.MAP) {
191
+ this.drawScalebar = this.drawMapScalebar;
192
+ } else {
193
+ this.drawScalebar = this.drawMicroscopyScalebar;
194
+ }
195
+ },
196
+ setMinWidth: function (minWidth) {
197
+ this.divElt.style.width = minWidth;
198
+ // Make sure to display the element before getting is width
199
+ this.divElt.style.display = "";
200
+ this.minWidth = this.divElt.offsetWidth;
201
+ },
202
+ /**
203
+ * Refresh the scalebar with the options submitted.
204
+ * @param {Object} options
205
+ * @param {OpenSeadragon.ScalebarType} options.type The scale bar type.
206
+ * Default: microscopy
207
+ * @param {Integer} options.pixelsPerMeter The pixels per meter of the
208
+ * zoomable image at the original image size. If null, the scale bar is not
209
+ * displayed. default: null
210
+ * @param {Integer} options.referenceItemIdx Specify the item from
211
+ * viewer.world to which options.pixelsPerMeter is refering.
212
+ * default: 0
213
+ * @param (String} options.minWidth The minimal width of the scale bar as a
214
+ * CSS string (ex: 100px, 1em, 1% etc...) default: 150px
215
+ * @param {OpenSeadragon.ScalebarLocation} options.location The location
216
+ * of the scale bar inside the viewer. default: bottom left
217
+ * @param {Integer} options.xOffset Offset location of the scale bar along x.
218
+ * default: 5
219
+ * @param {Integer} options.yOffset Offset location of the scale bar along y.
220
+ * default: 5
221
+ * @param {Boolean} options.stayInsideImage When set to true, keep the
222
+ * scale bar inside the image when zooming out. default: true
223
+ * @param {String} options.color The color of the scale bar using a color
224
+ * name or the hexadecimal format (ex: black or #000000) default: black
225
+ * @param {String} options.fontColor The font color. default: black
226
+ * @param {String} options.backgroundColor The background color. default: none
227
+ * @param {String} options.fontSize The font size. default: not set
228
+ * @param {String} options.barThickness The thickness of the scale bar in px.
229
+ * default: 2
230
+ * @param {function} options.sizeAndTextRenderer A function which will be
231
+ * called to determine the size of the scale bar and it's text content.
232
+ * The function must have 2 parameters: the PPM at the current zoom level
233
+ * and the minimum size of the scale bar. It must return an object containing
234
+ * 2 attributes: size and text containing the size of the scale bar and the text.
235
+ * default: $.ScalebarSizeAndTextRenderer.METRIC_LENGTH
236
+ */
237
+ refresh: function (options) {
238
+ this.updateOptions(options);
239
+
240
+ if (
241
+ !this.viewer.isOpen() ||
242
+ !this.drawScalebar ||
243
+ !this.pixelsPerMeter ||
244
+ !this.location
245
+ ) {
246
+ this.divElt.style.display = "none";
247
+ return;
248
+ }
249
+ this.divElt.style.display = "";
250
+
251
+ var viewport = this.viewer.viewport;
252
+ var tiledImage = this.viewer.world.getItemAt(this.referenceItemIdx);
253
+ var zoom = tiledImageViewportToImageZoom(
254
+ tiledImage,
255
+ viewport.getZoom(true)
256
+ );
257
+ var currentPPM = zoom * this.pixelsPerMeter;
258
+ var props = this.sizeAndTextRenderer(currentPPM, this.minWidth);
259
+
260
+ this.drawScalebar(props.size, props.text);
261
+ var location = this.getScalebarLocation();
262
+ this.divElt.style.left = location.x + "px";
263
+ this.divElt.style.top = location.y + "px";
264
+ },
265
+ drawMicroscopyScalebar: function (size, text) {
266
+ this.divElt.style.fontSize = this.fontSize;
267
+ this.divElt.style.fontFamily = this.fontFamily;
268
+ this.divElt.style.textAlign = "center";
269
+ this.divElt.style.color = this.fontColor;
270
+ this.divElt.style.border = "none";
271
+ this.divElt.style.borderBottom =
272
+ this.barThickness + "px solid " + this.color;
273
+ this.divElt.style.backgroundColor = this.backgroundColor;
274
+ this.divElt.innerHTML = text;
275
+ this.divElt.style.width = size + "px";
276
+ },
277
+ drawMapScalebar: function (size, text) {
278
+ this.divElt.style.fontSize = this.fontSize;
279
+ this.divElt.style.fontFamily = this.fontFamily;
280
+ this.divElt.style.textAlign = "center";
281
+ this.divElt.style.color = this.fontColor;
282
+ this.divElt.style.border = this.barThickness + "px solid " + this.color;
283
+ this.divElt.style.borderTop = "none";
284
+ this.divElt.style.backgroundColor = this.backgroundColor;
285
+ this.divElt.innerHTML = text;
286
+ this.divElt.style.width = size + "px";
287
+ },
288
+ /**
289
+ * Compute the location of the scale bar.
290
+ * @returns {OpenSeadragon.Point}
291
+ */
292
+ getScalebarLocation: function () {
293
+ if (this.location === $.ScalebarLocation.TOP_LEFT) {
294
+ var x = 0;
295
+ var y = 0;
296
+ if (this.stayInsideImage) {
297
+ var pixel = this.viewer.viewport.pixelFromPoint(
298
+ new $.Point(0, 0),
299
+ true
300
+ );
301
+ if (!this.viewer.wrapHorizontal) {
302
+ x = Math.max(pixel.x, 0);
303
+ }
304
+ if (!this.viewer.wrapVertical) {
305
+ y = Math.max(pixel.y, 0);
306
+ }
307
+ }
308
+ return new $.Point(x + this.xOffset, y + this.yOffset);
309
+ }
310
+ if (this.location === $.ScalebarLocation.TOP_RIGHT) {
311
+ var barWidth = this.divElt.offsetWidth;
312
+ var container = this.viewer.container;
313
+ var x = container.offsetWidth - barWidth;
314
+ var y = 0;
315
+ if (this.stayInsideImage) {
316
+ var pixel = this.viewer.viewport.pixelFromPoint(
317
+ new $.Point(1, 0),
318
+ true
319
+ );
320
+ if (!this.viewer.wrapHorizontal) {
321
+ x = Math.min(x, pixel.x - barWidth);
322
+ }
323
+ if (!this.viewer.wrapVertical) {
324
+ y = Math.max(y, pixel.y);
325
+ }
326
+ }
327
+ return new $.Point(x - this.xOffset, y + this.yOffset);
328
+ }
329
+ if (this.location === $.ScalebarLocation.BOTTOM_RIGHT) {
330
+ var barWidth = this.divElt.offsetWidth;
331
+ var barHeight = this.divElt.offsetHeight;
332
+ var container = this.viewer.container;
333
+ var x = container.offsetWidth - barWidth;
334
+ var y = container.offsetHeight - barHeight;
335
+ if (this.stayInsideImage) {
336
+ var pixel = this.viewer.viewport.pixelFromPoint(
337
+ new $.Point(1, 1 / this.viewer.source.aspectRatio),
338
+ true
339
+ );
340
+ if (!this.viewer.wrapHorizontal) {
341
+ x = Math.min(x, pixel.x - barWidth);
342
+ }
343
+ if (!this.viewer.wrapVertical) {
344
+ y = Math.min(y, pixel.y - barHeight);
345
+ }
346
+ }
347
+ return new $.Point(x - this.xOffset, y - this.yOffset);
348
+ }
349
+ if (this.location === $.ScalebarLocation.BOTTOM_LEFT) {
350
+ var barHeight = this.divElt.offsetHeight;
351
+ var container = this.viewer.container;
352
+ var x = 0;
353
+ var y = container.offsetHeight - barHeight;
354
+ if (this.stayInsideImage) {
355
+ var pixel = this.viewer.viewport.pixelFromPoint(
356
+ new $.Point(0, 1 / this.viewer.source.aspectRatio),
357
+ true
358
+ );
359
+ if (!this.viewer.wrapHorizontal) {
360
+ x = Math.max(x, pixel.x);
361
+ }
362
+ if (!this.viewer.wrapVertical) {
363
+ y = Math.min(y, pixel.y - barHeight);
364
+ }
365
+ }
366
+ return new $.Point(x + this.xOffset, y - this.yOffset);
367
+ }
368
+ },
369
+ /**
370
+ * Get the rendered scalebar in a canvas.
371
+ * @returns {Element} A canvas containing the scalebar representation
372
+ */
373
+ getAsCanvas: function () {
374
+ var canvas = document.createElement("canvas");
375
+ canvas.width = this.divElt.offsetWidth;
376
+ canvas.height = this.divElt.offsetHeight;
377
+ var context = canvas.getContext("2d");
378
+ context.fillStyle = this.backgroundColor;
379
+ context.fillRect(0, 0, canvas.width, canvas.height);
380
+ context.fillStyle = this.color;
381
+ context.fillRect(
382
+ 0,
383
+ canvas.height - this.barThickness,
384
+ canvas.width,
385
+ canvas.height
386
+ );
387
+ if (this.drawScalebar === this.drawMapScalebar) {
388
+ context.fillRect(0, 0, this.barThickness, canvas.height);
389
+ context.fillRect(
390
+ canvas.width - this.barThickness,
391
+ 0,
392
+ this.barThickness,
393
+ canvas.height
394
+ );
395
+ }
396
+ context.font = window.getComputedStyle(this.divElt).font;
397
+ context.textAlign = "center";
398
+ context.textBaseline = "middle";
399
+ context.fillStyle = this.fontColor;
400
+ var hCenter = canvas.width / 2;
401
+ var vCenter = canvas.height / 2;
402
+ context.fillText(this.divElt.textContent, hCenter, vCenter);
403
+ return canvas;
404
+ },
405
+ /**
406
+ * Get a copy of the current OpenSeadragon canvas with the scalebar.
407
+ * @returns {Element} A canvas containing a copy of the current OpenSeadragon canvas with the scalebar
408
+ */
409
+ getImageWithScalebarAsCanvas: function () {
410
+ var imgCanvas = this.viewer.drawer.canvas;
411
+ var newCanvas = document.createElement("canvas");
412
+ newCanvas.width = imgCanvas.width;
413
+ newCanvas.height = imgCanvas.height;
414
+ var newCtx = newCanvas.getContext("2d");
415
+ newCtx.drawImage(imgCanvas, 0, 0);
416
+ var scalebarCanvas = this.getAsCanvas();
417
+ var location = this.getScalebarLocation();
418
+ newCtx.drawImage(scalebarCanvas, location.x, location.y);
419
+ return newCanvas;
420
+ },
421
+ };
422
+
423
+ $.ScalebarSizeAndTextRenderer = {
424
+ /**
425
+ * Metric length. From nano meters to kilometers.
426
+ */
427
+ METRIC_LENGTH: function (ppm, minSize) {
428
+ return getScalebarSizeAndTextForMetric(ppm, minSize, "m");
429
+ },
430
+ /**
431
+ * Imperial length. Choosing the best unit from thou, inch, foot and mile.
432
+ */
433
+ IMPERIAL_LENGTH: function (ppm, minSize) {
434
+ var maxSize = minSize * 2;
435
+ var ppi = ppm * 0.0254;
436
+ if (maxSize < ppi * 12) {
437
+ if (maxSize < ppi) {
438
+ var ppt = ppi / 1000;
439
+ return getScalebarSizeAndText(ppt, minSize, "th");
440
+ }
441
+ return getScalebarSizeAndText(ppi, minSize, "in");
442
+ }
443
+ var ppf = ppi * 12;
444
+ if (maxSize < ppf * 2000) {
445
+ return getScalebarSizeAndText(ppf, minSize, "ft");
446
+ }
447
+ var ppmi = ppf * 5280;
448
+ return getScalebarSizeAndText(ppmi, minSize, "mi");
449
+ },
450
+ /**
451
+ * Astronomy units. Choosing the best unit from arcsec, arcminute, and degree
452
+ */
453
+ ASTRONOMY: function (ppa, minSize) {
454
+ var maxSize = minSize * 2;
455
+ if (maxSize < ppa * 60) {
456
+ return getScalebarSizeAndText(ppa, minSize, '"', false, "");
457
+ }
458
+ var ppminutes = ppa * 60;
459
+ if (maxSize < ppminutes * 60) {
460
+ return getScalebarSizeAndText(ppminutes, minSize, "'", false, "");
461
+ }
462
+ var ppd = ppminutes * 60;
463
+ return getScalebarSizeAndText(ppd, minSize, "&#176", false, "");
464
+ },
465
+ /**
466
+ * Standard time. Choosing the best unit from second (and metric divisions),
467
+ * minute, hour, day and year.
468
+ */
469
+ STANDARD_TIME: function (pps, minSize) {
470
+ var maxSize = minSize * 2;
471
+ if (maxSize < pps * 60) {
472
+ return getScalebarSizeAndTextForMetric(pps, minSize, "s", false);
473
+ }
474
+ var ppminutes = pps * 60;
475
+ if (maxSize < ppminutes * 60) {
476
+ return getScalebarSizeAndText(ppminutes, minSize, "minute", true);
477
+ }
478
+ var pph = ppminutes * 60;
479
+ if (maxSize < pph * 24) {
480
+ return getScalebarSizeAndText(pph, minSize, "hour", true);
481
+ }
482
+ var ppd = pph * 24;
483
+ if (maxSize < ppd * 365.25) {
484
+ return getScalebarSizeAndText(ppd, minSize, "day", true);
485
+ }
486
+ var ppy = ppd * 365.25;
487
+ return getScalebarSizeAndText(ppy, minSize, "year", true);
488
+ },
489
+ /**
490
+ * Generic metric unit. One can use this function to create a new metric
491
+ * scale. For example, here is an implementation of energy levels:
492
+ * function(ppeV, minSize) {
493
+ * return OpenSeadragon.ScalebarSizeAndTextRenderer.METRIC_GENERIC(
494
+ * ppeV, minSize, "eV");
495
+ * }
496
+ */
497
+ METRIC_GENERIC: getScalebarSizeAndTextForMetric,
498
+ };
499
+
500
+ // Missing TiledImage.viewportToImageZoom function in OSD 2.0.0
501
+ function tiledImageViewportToImageZoom(tiledImage, viewportZoom) {
502
+ var ratio =
503
+ (tiledImage._scaleSpring.current.value *
504
+ tiledImage.viewport._containerInnerSize.x) /
505
+ tiledImage.source.dimensions.x;
506
+ return ratio * viewportZoom;
507
+ }
508
+
509
+ function getScalebarSizeAndText(
510
+ ppm,
511
+ minSize,
512
+ unitSuffix,
513
+ handlePlural,
514
+ spacer
515
+ ) {
516
+ spacer = spacer === undefined ? " " : spacer;
517
+ var value = normalize(ppm, minSize);
518
+ var factor = roundSignificand((value / ppm) * minSize, 3);
519
+ var size = value * minSize;
520
+ var plural = handlePlural && factor > 1 ? "s" : "";
521
+ return {
522
+ size: size,
523
+ text: factor + spacer + unitSuffix + plural,
524
+ };
525
+ }
526
+
527
+ function getScalebarSizeAndTextForMetric(ppm, minSize, unitSuffix) {
528
+ var value = normalize(ppm, minSize);
529
+ var factor = roundSignificand((value / ppm) * minSize, 3);
530
+ var size = value * minSize;
531
+ var valueWithUnit = getWithUnit(factor, unitSuffix);
532
+ return {
533
+ size: size,
534
+ text: valueWithUnit,
535
+ };
536
+ }
537
+
538
+ function normalize(value, minSize) {
539
+ var significand = getSignificand(value);
540
+ var minSizeSign = getSignificand(minSize);
541
+ var result = getSignificand(significand / minSizeSign);
542
+ if (result >= 5) {
543
+ result /= 5;
544
+ }
545
+ if (result >= 4) {
546
+ result /= 4;
547
+ }
548
+ if (result >= 2) {
549
+ result /= 2;
550
+ }
551
+ return result;
552
+ }
553
+
554
+ function getSignificand(x) {
555
+ return x * Math.pow(10, Math.ceil(-log10(x)));
556
+ }
557
+
558
+ function roundSignificand(x, decimalPlaces) {
559
+ var exponent = -Math.ceil(-log10(x));
560
+ var power = decimalPlaces - exponent;
561
+ var significand = x * Math.pow(10, power);
562
+ // To avoid rounding problems, always work with integers
563
+ if (power < 0) {
564
+ return Math.round(significand) * Math.pow(10, -power);
565
+ }
566
+ return Math.round(significand) / Math.pow(10, power);
567
+ }
568
+
569
+ function log10(x) {
570
+ return Math.log(x) / Math.log(10);
571
+ }
572
+
573
+ function getWithUnit(value, unitSuffix) {
574
+ if (value < 0.000001) {
575
+ return value * 1000000000 + " n" + unitSuffix;
576
+ }
577
+ if (value < 0.001) {
578
+ return value * 1000000 + " μ" + unitSuffix;
579
+ }
580
+ if (value < 1) {
581
+ return value * 1000 + " m" + unitSuffix;
582
+ }
583
+ if (value >= 1000) {
584
+ return value / 1000 + " k" + unitSuffix;
585
+ }
586
+ return value + " " + unitSuffix;
587
+ }
588
+
589
+ function isDefined(variable) {
590
+ return typeof variable !== "undefined";
591
+ }
592
+ })(OpenSeadragon);