From 5961baf59efcb541a2e3938ae89942c6dba91ffe Mon Sep 17 00:00:00 2001 From: PJ-568 Date: Mon, 6 Nov 2023 13:09:21 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20release=20from=20@=20PJ-568/56?= =?UTF-8?q?8tools@2f50818efa31f51dd49703e7f1b3245fcf27b95a=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../add-gif-into-a-still-image/index.html | 2 +- .../tuts/add-watermark-to-photo/index.html | 2 +- .../tuts/convert-sketch-to-psd/index.html | 2 +- .../remove-background-from-photo/index.html | 2 +- lib/tools/dwv/demo/stable/dist/dwv.d.ts | 585 ++++++++--- lib/tools/dwv/demo/stable/dist/dwv.min.js | 2 +- lib/tools/dwv/demo/stable/dist/dwv.min.js.map | 2 +- .../demo/stable/node_modules/.yarn-integrity | 28 +- lib/tools/dwv/demo/stable/viewer.js | 2 +- .../dwv/demo/trunk/decoders/pdfjs/readme.md | 27 +- .../demo/trunk/decoders/rii-mango/readme.md | 21 +- lib/tools/dwv/demo/trunk/dist/dwv.d.ts | 444 ++++++-- lib/tools/dwv/demo/trunk/dist/dwv.min.js | 2 +- lib/tools/dwv/demo/trunk/dist/dwv.min.js.map | 2 +- .../demo/trunk/node_modules/.yarn-integrity | 190 ++-- lib/tools/dwv/demo/trunk/viewer.html | 2 +- lib/tools/dwv/demo/trunk/viewer.js | 2 +- sitemap.xml | 994 +++++++++--------- 18 files changed, 1480 insertions(+), 831 deletions(-) diff --git a/lib/tools/Open-Photopea/tuts/add-gif-into-a-still-image/index.html b/lib/tools/Open-Photopea/tuts/add-gif-into-a-still-image/index.html index 912a01f6..89b6e447 100644 --- a/lib/tools/Open-Photopea/tuts/add-gif-into-a-still-image/index.html +++ b/lib/tools/Open-Photopea/tuts/add-gif-into-a-still-image/index.html @@ -1 +1 @@ -Add GIF Into a Still Image
June 8, 2018

Add GIF Into a Still Image

Do you want to decorate a still image with an animated GIF? Here is how to do it fast and for free :)

Open your images

First, go to Photopea.com. Press File - Open, and open your still image. Then, press File - Open again, and open your GIF. You should see two panels ("tabs") in Photopea.

Duplicate layers

Now, we need to copy all layers of the second document (frames of animation) into the first document (still image). Look into the Layers panel (bottom right): there is a folder (named "animated" in our case), that contains all frame layers.

Now, right-click that folder (in Layers panel) and choose Duplicate Into.

In the new window, choose the document with the still image as a destination. Confirm with OK button.

Move the GIF to the right spot

Switch to the panel ("tab") with a still image. You will see your GIF in the top left corner. Now, click and drag it to a new position (notice, that all layers of the animation are still highlighted, so you are moving all frames at the same time).

You can even use Free Transform to make the GIF larger / smaller, or rotate it, etc.

Now, press File - Export As - GIF, and ... a new animation is ready! Hit Save to save it :)

+Add GIF Into a Still Image
June 8, 2018

Add GIF Into a Still Image

Do you want to decorate a still image with an animated GIF? Here is how to do it fast and for free :)

Open your images

First, go to Photopea.com. Press File - Open, and open your still image. Then, press File - Open again, and open your GIF. You should see two panels ("tabs") in Photopea.

Duplicate layers

Now, we need to copy all layers of the second document (frames of animation) into the first document (still image). Look into the Layers panel (bottom right): there is a folder (named "animated" in our case), that contains all frame layers.

Now, right-click that folder (in Layers panel) and choose Duplicate Into.

In the new window, choose the document with the still image as a destination. Confirm with OK button.

Move the GIF to the right spot

Switch to the panel ("tab") with a still image. You will see your GIF in the top left corner. Now, click and drag it to a new position (notice, that all layers of the animation are still highlighted, so you are moving all frames at the same time).

You can even use Free Transform to make the GIF larger / smaller, or rotate it, etc.

Now, press File - Export As - GIF, and ... a new animation is ready! Hit Save to save it :)

diff --git a/lib/tools/Open-Photopea/tuts/add-watermark-to-photo/index.html b/lib/tools/Open-Photopea/tuts/add-watermark-to-photo/index.html index 4867b8ce..131db1a9 100644 --- a/lib/tools/Open-Photopea/tuts/add-watermark-to-photo/index.html +++ b/lib/tools/Open-Photopea/tuts/add-watermark-to-photo/index.html @@ -1 +1 @@ -Add Watermark to a Photo
September 6, 2018

Add Watermark to a Photo

Sometimes, we need to place a watermark into a photo. How can we do it fast and for free?

Open Your Image

First, go to Photopea.com. Press File - Open, and open open your main (background) image.

Make a Watermark

Now, we need an element, that we will use as a watermark. We have several options:

  • Load another image: Press File - Open & Place. Open your image, and then hit Enter to confirm placing.
  • Make a watermark yourself: E.g. use a Brush or Vector tools to draw a circle or other interesting shapes.
  • Type a text: Use a Type tool to type your own text.

Once we are done, right-click the watermark layer (in Layers panel) and choose Rasterize.

Optional: we can also adjust the colors. Press Image - Adjustments - Hue/Saturation. Drag the Lightness to make the watermark completely black or completely white. Confirm with OK.

Position the Watermark

We may want our watermark to be in the corner of the image, or to be repeated over the whole image as a pattern. To move it to the corner, choose a Move tool (the first in the Toolbar) and drag the watermark to a correct position. You can also use Free Transform to make it bigger or smaller.

To repeat the watermark across the whole image, press Filter - Other - Repeat. Here, you can choose many parameters, such as Scale, Row Shift, Spacing, Angle etc. Confirm the filter by pressing OK.

Change the Opacity

Once we have the right watermark at the right position, we can change the way it interacts with a background. The main two parameters are the Opacity and the Blend Mode, which you can change at the top of the Layers panel. Useful blend modes are Multiply, Screen and Overlay.

Now, press File - Export As - PNG or JPG. And we are done! :)

+Add Watermark to a Photo
September 6, 2018

Add Watermark to a Photo

Sometimes, we need to place a watermark into a photo. How can we do it fast and for free?

Open Your Image

First, go to Photopea.com. Press File - Open, and open open your main (background) image.

Make a Watermark

Now, we need an element, that we will use as a watermark. We have several options:

  • Load another image: Press File - Open & Place. Open your image, and then hit Enter to confirm placing.
  • Make a watermark yourself: E.g. use a Brush or Vector tools to draw a circle or other interesting shapes.
  • Type a text: Use a Type tool to type your own text.

Once we are done, right-click the watermark layer (in Layers panel) and choose Rasterize.

Optional: we can also adjust the colors. Press Image - Adjustments - Hue/Saturation. Drag the Lightness to make the watermark completely black or completely white. Confirm with OK.

Position the Watermark

We may want our watermark to be in the corner of the image, or to be repeated over the whole image as a pattern. To move it to the corner, choose a Move tool (the first in the Toolbar) and drag the watermark to a correct position. You can also use Free Transform to make it bigger or smaller.

To repeat the watermark across the whole image, press Filter - Other - Repeat. Here, you can choose many parameters, such as Scale, Row Shift, Spacing, Angle etc. Confirm the filter by pressing OK.

Change the Opacity

Once we have the right watermark at the right position, we can change the way it interacts with a background. The main two parameters are the Opacity and the Blend Mode, which you can change at the top of the Layers panel. Useful blend modes are Multiply, Screen and Overlay.

Now, press File - Export As - PNG or JPG. And we are done! :)

diff --git a/lib/tools/Open-Photopea/tuts/convert-sketch-to-psd/index.html b/lib/tools/Open-Photopea/tuts/convert-sketch-to-psd/index.html index f5931465..f0c85fe4 100644 --- a/lib/tools/Open-Photopea/tuts/convert-sketch-to-psd/index.html +++ b/lib/tools/Open-Photopea/tuts/convert-sketch-to-psd/index.html @@ -1 +1 @@ -Convert Sketch to PSD
May 30, 2018

Convert Sketch to PSD

Sometimes, we want to work with Sketch graphics in Photoshop. Converting it manually is hard, but it can be done automatically, see how!

Sketch to PSD

Today (the middle of 2018), Photopea is the best tool to convert Sketch graphics to Photoshop files. Go to Photopea.com to start a free online photo editor. If you see a Welcome Screen, you can close it by pressing the Cross in the top right corner.

Open a Sketch file from your computer: click File - Open (in the top left corner), then find and select the PSD file (or use drag and drop).

You should see the graphics of the Sketch file in front of you. Now, just click File - Save As PSD. And that's all! So simple :)

+Convert Sketch to PSD
May 30, 2018

Convert Sketch to PSD

Sometimes, we want to work with Sketch graphics in Photoshop. Converting it manually is hard, but it can be done automatically, see how!

Sketch to PSD

Today (the middle of 2018), Photopea is the best tool to convert Sketch graphics to Photoshop files. Go to Photopea.com to start a free online photo editor. If you see a Welcome Screen, you can close it by pressing the Cross in the top right corner.

Open a Sketch file from your computer: click File - Open (in the top left corner), then find and select the PSD file (or use drag and drop).

You should see the graphics of the Sketch file in front of you. Now, just click File - Save As PSD. And that's all! So simple :)

diff --git a/lib/tools/Open-Photopea/tuts/remove-background-from-photo/index.html b/lib/tools/Open-Photopea/tuts/remove-background-from-photo/index.html index 1967d640..fa354e93 100644 --- a/lib/tools/Open-Photopea/tuts/remove-background-from-photo/index.html +++ b/lib/tools/Open-Photopea/tuts/remove-background-from-photo/index.html @@ -1 +1 @@ -Remove Background From Photo
May 30, 2018

Remove Background From Photo

Cut out an object from a photo, so you can place it elsewhere, or give it another background.

Go to Photopea.com to start a free online photo editor. If you see a Welcome Screen, you can close it by pressing the Cross in the top right corner.

Open a photo from your computer: click File - Open (in the top left corner), then find and select the file in your device (or use drag and drop).

We have to do two steps: Select Background (the hard part) and Delete selected area.

Select Background

Photopea lets you select some part of the image. This selected area is illustrated by a countour around it.

We will demonstrate our work on this image of a tree, where we want to remove the sky. Properly selected sky will look like this:

We will select the bacgkround with a Magic Wand tool. It can select an area with similar colors (blue in our case). First, select Magic Wand in the toolbar on the left (two tools are hidden under the same button).

Now, click on the sky area to select it. Each click removes the old selection, and creates a new one. Hold Shift to combine the new selection with an old selection. Try changing the Tolerance and Contiguous in the top menu. You should end up with a selected sky, as you see above.

Selecting may be hard, it can take 10 seconds, or even several hours. The more time you spend, the more prefect it becomes. Read more about selections at /learn/selections and its sub-articles.

Delete selected area

To delete a selected area, press Edit - Clear in the top menu, or Delete on your keyboard. The white and grey squares mean, that the area is empty.

Now, save the image as PNG (File - Export As - PNG, hit "Save"). PNG is necessary, because JPG does not support transparent pixels.

+Remove Background From Photo
May 30, 2018

Remove Background From Photo

Cut out an object from a photo, so you can place it elsewhere, or give it another background.

Go to Photopea.com to start a free online photo editor. If you see a Welcome Screen, you can close it by pressing the Cross in the top right corner.

Open a photo from your computer: click File - Open (in the top left corner), then find and select the file in your device (or use drag and drop).

We have to do two steps: Select Background (the hard part) and Delete selected area.

Select Background

Photopea lets you select some part of the image. This selected area is illustrated by a countour around it.

We will demonstrate our work on this image of a tree, where we want to remove the sky. Properly selected sky will look like this:

We will select the bacgkround with a Magic Wand tool. It can select an area with similar colors (blue in our case). First, select Magic Wand in the toolbar on the left (two tools are hidden under the same button).

Now, click on the sky area to select it. Each click removes the old selection, and creates a new one. Hold Shift to combine the new selection with an old selection. Try changing the Tolerance and Contiguous in the top menu. You should end up with a selected sky, as you see above.

Selecting may be hard, it can take 10 seconds, or even several hours. The more time you spend, the more prefect it becomes. Read more about selections at /learn/selections and its sub-articles.

Delete selected area

To delete a selected area, press Edit - Clear in the top menu, or Delete on your keyboard. The white and grey squares mean, that the area is empty.

Now, save the image as PNG (File - Export As - PNG, hit "Save"). PNG is necessary, because JPG does not support transparent pixels.

diff --git a/lib/tools/dwv/demo/stable/dist/dwv.d.ts b/lib/tools/dwv/demo/stable/dist/dwv.d.ts index 2b9a1b19..b6b23b00 100644 --- a/lib/tools/dwv/demo/stable/dist/dwv.d.ts +++ b/lib/tools/dwv/demo/stable/dist/dwv.d.ts @@ -6,6 +6,11 @@ */ export declare function addTagsToDictionary(group: string, tags: object): void; +/** + * List of ViewConfigs indexed by dataIds. + * + * @typedef {Object} DataViewConfigs + */ /** * Main application class. * @@ -13,9 +18,10 @@ export declare function addTagsToDictionary(group: string, tags: object): void; * // create the dwv app * const app = new dwv.App(); * // initialise - * app.init({ - * dataViewConfigs: {'*': [{divId: 'layerGroup0'}]} - * }); + * const viewConfig0 = new ViewConfig('layerGroup0'); + * const viewConfigs = {'*': [viewConfig0]}; + * const options = new AppOptions(viewConfigs); + * app.init(options); * // load dicom data * app.loadURLs([ * 'https://raw.githubusercontent.com/ivmartel/dwv/master/tests/data/bbmri-53323851.dcm' @@ -102,9 +108,9 @@ export declare class App { /** * Get the toolbox controller. * - * @returns {object} The controller. + * @returns {ToolboxController} The controller. */ - getToolboxController(): object; + getToolboxController(): ToolboxController; /** * Get the active layer group. * The layer is available after the first loaded item. @@ -117,17 +123,17 @@ export declare class App { * The layer are available after the first loaded item. * * @param {number} index The data index. - * @returns {Array} The layers. + * @returns {ViewLayer[]} The layers. */ - getViewLayersByDataIndex(index: number): any[]; + getViewLayersByDataIndex(index: number): ViewLayer[]; /** * Get the draw layers associated to a data index. * The layer are available after the first loaded item. * * @param {number} index The data index. - * @returns {Array} The layers. + * @returns {DrawLayer[]} The layers. */ - getDrawLayersByDataIndex(index: number): any[]; + getDrawLayersByDataIndex(index: number): DrawLayer[]; /** * Get a layer group by div id. * The layer is available after the first loaded item. @@ -153,33 +159,22 @@ export declare class App { * * @param {object} cmd The command to add. * @fires UndoStack#undoadd + * @function */ addToUndoStack: (cmd: object) => void; /** * Initialise the application. * - * @param {object} opt The application option with: - * - `dataViewConfigs`: data indexed object containing the data view - * configurations in the form of a list of objects containing: - * - divId: the HTML div id - * - orientation: optional 'axial', 'coronal' or 'sagittal' orientation - * string (default undefined keeps the original slice order) - * - `binders`: array of layerGroup binders - * - `tools`: tool name indexed object containing individual tool - * configurations in the form of a list of objects containing: - * - options: array of tool options - * - `viewOnFirstLoadItem`: boolean flag to trigger the first data render - * after the first loaded data or not - * - `defaultCharacterSet`: the default chraracter set string used for DICOM - * parsing + * @param {AppOptions} opt The application options * @example * // create the dwv app * const app = new dwv.App(); * // initialise - * app.init({ - * dataViewConfigs: {'*': [{divId: 'layerGroup0'}]}, - * viewOnFirstLoadItem: false - * }); + * const viewConfig0 = new ViewConfig('layerGroup0'); + * const viewConfigs = {'*': [viewConfig0]}; + * const options = new AppOptions(viewConfigs); + * options.viewOnFirstLoadItem = false; + * app.init(options); * // render button * const button = document.createElement('button'); * button.id = 'render'; @@ -199,7 +194,7 @@ export declare class App { * 'https://raw.githubusercontent.com/ivmartel/dwv/master/tests/data/bbmri-53323851.dcm' * ]); */ - init(opt: object): void; + init(opt: AppOptions): void; /** * Reset the application. */ @@ -212,34 +207,35 @@ export declare class App { * Add an event listener to this class. * * @param {string} type The event type. - * @param {object} callback The method associated with the provided + * @param {Function} callback The function associated with the provided * event type, will be called with the fired event. */ - addEventListener(type: string, callback: object): void; + addEventListener(type: string, callback: Function): void; /** * Remove an event listener from this class. * * @param {string} type The event type. - * @param {object} callback The method associated with the provided + * @param {Function} callback The function associated with the provided * event type. */ - removeEventListener(type: string, callback: object): void; + removeEventListener(type: string, callback: Function): void; /** * Load a list of files. Can be image files or a state file. * - * @param {FileList} files The list of files to load. + * @param {File[]} files The list of files to load. * @fires App#loadstart * @fires App#loadprogress * @fires App#loaditem * @fires App#loadend * @fires App#loaderror * @fires App#loadabort + * @function */ - loadFiles: (files: FileList) => void; + loadFiles: (files: File[]) => void; /** * Load a list of URLs. Can be image files or a state file. * - * @param {Array} urls The list of urls to load. + * @param {string[]} urls The list of urls to load. * @param {object} [options] The options object, can contain: * - requestHeaders: an array of {name, value} to use as request headers * - withCredentials: boolean xhr.withCredentials flag to pass to the request @@ -250,13 +246,15 @@ export declare class App { * @fires App#loadend * @fires App#loaderror * @fires App#loadabort + * @function */ - loadURLs: (urls: any[], options?: object) => void; + loadURLs: (urls: string[], options?: object) => void; /** * Load from an input uri. * * @param {string} uri The input uri, for example: 'window.location.href'. * @param {object} [options] Optional url request options. + * @function */ loadFromUri: (uri: string, options?: object) => void; /** @@ -270,6 +268,7 @@ export declare class App { * @fires App#loadend * @fires App#loaderror * @fires App#loadabort + * @function */ loadImageObject: (data: any[]) => void; /** @@ -289,15 +288,20 @@ export declare class App { * Get the data view config. * Carefull, returns a reference, do not modify without resetting. * - * @returns {object} The configuration list. + * @returns {Object} The configuration list. */ - getDataViewConfig(): object; + getDataViewConfigs(): { + [x: string]: ViewConfig[]; + }; /** - * Set the data view configuration (see the init options for details). + * Set the data view configuration. + * Resets the stage and recreates all the views. * - * @param {object} configs The configuration list. + * @param {Object} configs The configuration list. */ - setDataViewConfig(configs: object): void; + setDataViewConfigs(configs: { + [x: string]: ViewConfig[]; + }): void; /** * Set the layer groups binders. * @@ -354,6 +358,8 @@ export declare class App { * Handle resize: fit the display to the window. * To be called once the image is loaded. * Can be connected to a window 'resize' event. + * + * @function */ onResize: () => void; /** @@ -361,6 +367,7 @@ export declare class App { * * @param {KeyboardEvent} event The key down event. * @fires App#keydown + * @function */ onKeydown: (event: KeyboardEvent) => void; /** @@ -375,6 +382,7 @@ export declare class App { * @param {KeyboardEvent} event The key down event. * @fires UndoStack#undo * @fires UndoStack#redo + * @function */ defaultOnKeydown: (event: KeyboardEvent) => void; /** @@ -388,9 +396,9 @@ export declare class App { /** * Set the colour map. * - * @param {string} colourMap The colour map name. + * @param {string} name The colour map name. */ - setColourMap(colourMap: string): void; + setColourMap(name: string): void; /** * Set the window/level preset. * @@ -436,6 +444,56 @@ export declare class App { #private; } +/** + * Application options. + */ +export declare class AppOptions { + /** + * @param {Object} dataViewConfigs DataId + * indexed object containing the data view configurations. + */ + constructor(dataViewConfigs: { + [x: string]: ViewConfig[]; + }); + /** + * DataId indexed object containing the data view configurations. + * + * @type {Object} + */ + dataViewConfigs: { + [x: string]: ViewConfig[]; + }; + /** + * Tool name indexed object containing individual tool configurations. + * + * @type {Object|undefined} + */ + tools: { + [x: string]: ToolConfig; + } | undefined; + /** + * Optional array of layerGroup binder names. + * + * @type {string[]|undefined} + */ + binders: string[] | undefined; + /** + * Optional boolean flag to trigger the first data render + * after the first loaded data or not. Defaults to true; + * + * @type {boolean|undefined} + */ + viewOnFirstLoadItem: boolean | undefined; + /** + * Optional default chraracter set string used for DICOM parsing if + * not passed in DICOM file. + * Valid values: https://developer.mozilla.org/en-US/docs/Web/API/Encoding_API/Encodings + * + * @type {string|undefined} + */ + defaultCharacterSet: string | undefined; +} + /** * Build a multipart message. * See: https://en.wikipedia.org/wiki/MIME#Multipart_messages @@ -448,6 +506,37 @@ export declare class App { */ export declare function buildMultipart(parts: any[], boundary: string): Uint8Array; +/** + * Colour map: red, green and blue components + * to associate with intensity values. + */ +export declare class ColourMap { + /** + * @param {number[]} red Red component. + * @param {number[]} green Green component. + * @param {number[]} blue Blue component. + */ + constructor(red: number[], green: number[], blue: number[]); + /** + * Red component: 256 values in the [0, 255] range. + * + * @type {number[]} + */ + red: number[]; + /** + * Green component: 256 values in the [0, 255] range. + * + * @type {number[]} + */ + green: number[]; + /** + * Blue component: 256 values in the [0, 255] range. + * + * @type {number[]} + */ + blue: number[]; +} + /** * Create an Image from DICOM elements. * @@ -483,6 +572,64 @@ export declare namespace customUI { export function openRoiDialog(data: any, callback: Function): void; } +/** + * DICOM data element. + */ +export declare class DataElement { + /** + * @param {string} vr The element VR (Value Representation). + */ + constructor(vr: string); + /** + * The element Value Representation. + * + * @type {string} + */ + vr: string; + /** + * The element value. + * + * @type {Array} + */ + value: any[]; + /** + * The element dicom tag. + * + * @type {Tag} + */ + tag: Tag; + /** + * The element Value Length. + * + * @type {number} + */ + vl: number; + /** + * Flag to know if defined or undefined sequence length. + * + * @type {boolean} + */ + undefinedLength: boolean; + /** + * The element start offset. + * + * @type {number} + */ + startOffset: number; + /** + * The element end offset. + * + * @type {number} + */ + endOffset: number; + /** + * The sequence items. + * + * @type {Array} + */ + items: any[]; +} + /** * Decoder scripts to be passed to web workers for image decoding. */ @@ -553,14 +700,16 @@ export declare class DicomParser { */ setDecoderCharacterSet(characterSet: string): void; /** - * Get the raw DICOM data elements. + * Get the DICOM data elements. * - * @returns {object} The raw DICOM elements. + * @returns {Object} The data elements. */ - getDicomElements(): object; + getDicomElements(): { + [x: string]: DataElement; + }; /** * Parse the complete DICOM file (given as input to the class). - * Fills in the member object 'dicomElements'. + * Fills in the member object 'dataElements'. * * @param {ArrayBuffer} buffer The input array buffer. */ @@ -571,16 +720,27 @@ export declare class DicomParser { /** * DICOM writer. * - * Example usage: + * @example + * // XMLHttpRequest onload callback + * const onload = function (event) { * const parser = new DicomParser(); - * parser.parse(this.response); - * + * parser.parse(event.target.response); + * // create writer with parser data elements * const writer = new DicomWriter(parser.getDicomElements()); + * // create modified buffer and put it in a Blol * const blob = new Blob([writer.getBuffer()], {type: 'application/dicom'}); - * + * // example download link * const element = document.getElementById("download"); * element.href = URL.createObjectURL(blob); * element.download = "anonym.dcm"; + * }; + * // DICOM file request + * const request = new XMLHttpRequest(); + * const url = 'https://raw.githubusercontent.com/ivmartel/dwv/master/tests/data/bbmri-53323851.dcm'; + * request.open('GET', url); + * request.responseType = 'arraybuffer'; + * request.onload = onload; + * request.send(); */ export declare class DicomWriter { /** @@ -591,10 +751,16 @@ export declare class DicomWriter { setUseUnVrForPrivateSq(flag: boolean): void; /** * Set the writing rules. + * List of writer rules indexed by either `default`, tagName or groupName. + * Each DICOM element will be checked to see if a rule is applicable. + * First checked by tagName and then by groupName, + * if nothing is found the default rule is applied. * - * @param {object} rules The input rules. + * @param {Object} rules The input rules. */ - setRules(rules: object): void; + setRules(rules: { + [x: string]: WriterRule; + }): void; /** * Use a TextEncoder instead of the default text decoder. */ @@ -603,13 +769,23 @@ export declare class DicomWriter { * Use default anonymisation rules. */ useDefaultAnonymisationRules(): void; + /** + * Get the element to write according to the class rules. + * Priority order: tagName, groupName, default. + * + * @param {DataElement} element The element to check + * @returns {DataElement|null} The element to write, can be null. + */ + getElementToWrite(element: DataElement): DataElement | null; /** * Get the ArrayBuffer corresponding to input DICOM elements. * - * @param {Array} dicomElements The wrapped elements to write. + * @param {Object} dataElements The elements to write. * @returns {ArrayBuffer} The elements as a buffer. */ - getBuffer(dicomElements: any[]): ArrayBuffer; + getBuffer(dataElements: { + [x: string]: DataElement; + }): ArrayBuffer; #private; } @@ -688,9 +864,9 @@ export declare class DrawLayer { * Set the layer scale. * * @param {object} newScale The scale as {x,y}. - * @param {Point3D} center The scale center. + * @param {Point3D} [center] The scale center. */ - setScale(newScale: object, center: Point3D): void; + setScale(newScale: object, center?: Point3D): void; /** * Set the layer offset. * @@ -751,7 +927,7 @@ export declare class DrawLayer { * @param {string} id The id of the group. * @returns {boolean} False if the group cannot be found. */ - toogleGroupVisibility(id: string): boolean; + toggleGroupVisibility(id: string): boolean; /** * Delete a Draw from the stage. * @@ -787,18 +963,18 @@ export declare class DrawLayer { * Add an event listener to this class. * * @param {string} type The event type. - * @param {object} callback The method associated with the provided + * @param {Function} callback The function associated with the provided * event type, will be called with the fired event. */ - addEventListener(type: string, callback: object): void; + addEventListener(type: string, callback: Function): void; /** * Remove an event listener from this class. * * @param {string} type The event type. - * @param {object} callback The method associated with the provided + * @param {Function} callback The function associated with the provided * event type. */ - removeEventListener(type: string, callback: object): void; + removeEventListener(type: string, callback: Function): void; #private; } @@ -984,6 +1160,12 @@ export declare class Geometry { #private; } +/** + * List of DICOM data elements indexed via a 8 character string formed from + * the group and element numbers. + * + * @typedef {Object} DataElements + */ /** * Get the version of the library. * @@ -992,15 +1174,20 @@ export declare class Geometry { export declare function getDwvVersion(): string; /** - * Get the DICOM elements from a DICOM json tags object. + * Get the DICOM elements from a 'simple' DICOM json tags object. * The json is a simplified version of the oficial DICOM json with * tag names instead of keys and direct values (no value property) for - * simple tags. + * simple tags. See synthetic test data (in tests/dicom) for examples. * - * @param {object} jsonTags The DICOM json tags object. - * @returns {object} The DICOM elements. + * @param {Object} jsonTags The DICOM + * json tags object. + * @returns {Object} The DICOM elements. */ -export declare function getElementsFromJSONTags(jsonTags: object): object; +export declare function getElementsFromJSONTags(jsonTags: { + [x: string]: any; +}): { + [x: string]: DataElement; +}; /** * Get the name of an image orientation patient. @@ -1058,6 +1245,16 @@ export declare function getTypedArray(bitsAllocated: number, pixelRepresentation */ export declare function getUID(tagName: string): string; +/** + * Check that an input buffer includes the DICOM prefix 'DICM' + * after the 128 bytes preamble. + * Ref: [DICOM File Meta]{@link https://dicom.nema.org/dicom/2013/output/chtml/part10/chapter_7.html#sect_7.1} + * + * @param {ArrayBuffer} buffer The buffer to check. + * @returns {boolean} True if the buffer includes the prefix. + */ +export declare function hasDicomPrefix(buffer: ArrayBuffer): boolean; + export declare namespace i18n { /** * Get the translated text. @@ -1082,7 +1279,7 @@ export declare namespace i18n { * const dicomParser = new dwv.DicomParser(); * dicomParser.parse(event.target.response); * // create the image object - * const image = createImage(dicomParser.getDicomElements()); + * const image = dwv.createImage(dicomParser.getDicomElements()); * // result div * const div = document.getElementById('dwv'); * // display the image size @@ -1301,18 +1498,18 @@ declare class Image_2 { * Add an event listener to this class. * * @param {string} type The event type. - * @param {object} callback The method associated with the provided + * @param {Function} callback The function associated with the provided * event type, will be called with the fired event. */ - addEventListener(type: string, callback: object): void; + addEventListener(type: string, callback: Function): void; /** * Remove an event listener from this class. * * @param {string} type The event type. - * @param {object} callback The method associated with the provided + * @param {Function} callback The function associated with the provided * event type. */ - removeEventListener(type: string, callback: object): void; + removeEventListener(type: string, callback: Function): void; /** * Set the inner buffer values at given offsets. * @@ -1454,9 +1651,9 @@ export { Image_2 as Image } */ export declare class Index { /** - * @param {Array} values The index values. + * @param {number[]} values The index values. */ - constructor(values: any[]); + constructor(values: number[]); /** * Get the index value at the given array index. * @@ -1479,9 +1676,9 @@ export declare class Index { /** * Get the values of this index. * - * @returns {Array} The array of values. + * @returns {number[]} The array of values. */ - getValues(): any[]; + getValues(): number[]; /** * Check if the input index can be compared to this one. * @@ -1500,9 +1697,9 @@ export declare class Index { * Compare indices and return different dimensions. * * @param {Index} rhs The index to compare to. - * @returns {Array} The list of different dimensions. + * @returns {number[]} The list of different dimensions. */ - compare(rhs: Index): any[]; + compare(rhs: Index): number[]; /** * Add another index to this one. * @@ -1521,10 +1718,10 @@ export declare class Index { /** * Get a string id from the index values in the form of: '#0-1_#1-2'. * - * @param {Array} [dims] Optional list of dimensions to use. + * @param {number[]} [dims] Optional list of dimensions to use. * @returns {string} The string id. */ - toStringId(dims?: any[]): string; + toStringId(dims?: number[]): string; #private; } @@ -1692,6 +1889,7 @@ export declare class LayerGroup { * Update layers (but not the active view layer) to a position change. * * @param {object} event The position change event. + * @function */ updateLayersToPositionChange: (event: object) => void; /** @@ -1762,18 +1960,18 @@ export declare class LayerGroup { * Add an event listener to this class. * * @param {string} type The event type. - * @param {object} callback The method associated with the provided + * @param {Function} callback The function associated with the provided * event type, will be called with the fired event. */ - addEventListener(type: string, callback: object): void; + addEventListener(type: string, callback: Function): void; /** * Remove an event listener from this class. * * @param {string} type The event type. - * @param {object} callback The method associated with the provided + * @param {Function} callback The function associated with the provided * event type. */ - removeEventListener(type: string, callback: object): void; + removeEventListener(type: string, callback: Function): void; #private; } @@ -1796,14 +1994,10 @@ export declare namespace logger { /** * List of available lookup tables (lut). * - * @type {Object} + * @type {Object} */ -export declare const lut: { - [x: string]: { - red: number[]; - green: number[]; - blue: number[]; - }; +export declare const luts: { + [x: string]: ColourMap; }; /** @@ -1921,9 +2115,9 @@ export declare class Matrix33 { */ export declare class Point { /** - * @param {Array} values The point values. + * @param {number[]} values The point values. */ - constructor(values: any[]); + constructor(values: number[]); /** * Get the index value at the given array index. * @@ -1946,9 +2140,9 @@ export declare class Point { /** * Get the values of this index. * - * @returns {Array} The array of values. + * @returns {number[]} The array of values. */ - getValues(): any[]; + getValues(): number[]; /** * Check if the input point can be compared to this one. * @@ -1967,9 +2161,9 @@ export declare class Point { * Compare points and return different dimensions. * * @param {Point} rhs The point to compare to. - * @returns {Array} The list of different dimensions. + * @returns {number[]} The list of different dimensions. */ - compare(rhs: Point): any[]; + compare(rhs: Point): number[]; /** * Get the 3D part of this point. * @@ -2470,10 +2664,91 @@ export declare class TagValueExtractor { /** * Get the time. * - * @param {object} _elements The DICOM elements. + * @param {Object} _elements The DICOM elements. * @returns {number|undefined} The time value if available. */ - getTime(_elements: object): number | undefined; + getTime(_elements: { + [x: string]: DataElement; + }): number | undefined; +} + +/** + * Toolbox controller. + */ +export declare class ToolboxController { + /** + * @param {object} toolList The list of tool objects. + */ + constructor(toolList: object); + /** + * Initialise. + */ + init(): void; + /** + * Get the tool list. + * + * @returns {Array} The list of tool objects. + */ + getToolList(): any[]; + /** + * Check if a tool is in the tool list. + * + * @param {string} name The name to check. + * @returns {boolean} The tool list element for the given name. + */ + hasTool(name: string): boolean; + /** + * Get the selected tool. + * + * @returns {object} The selected tool. + */ + getSelectedTool(): object; + /** + * Get the selected tool event handler. + * + * @param {string} eventType The event type, for example + * mousedown, touchstart... + * @returns {Function} The event handler. + */ + getSelectedToolEventHandler(eventType: string): Function; + /** + * Set the selected tool. + * + * @param {string} name The name of the tool. + */ + setSelectedTool(name: string): void; + /** + * Set the selected tool live features. + * + * @param {object} list The list of features. + */ + setToolFeatures(list: object): void; + /** + * Listen to layer interaction events. + * + * @param {object} layer The layer to listen to. + * @param {string} layerGroupDivId The associated layer group div id. + */ + bindLayer(layer: object, layerGroupDivId: string): void; + #private; +} + +/** + * Tool configuration. + */ +export declare class ToolConfig { + /** + * @param {string[]} [options] Optional tool options. + */ + constructor(options?: string[]); + /** + * Optional tool options. + * For Draw: list of shape names. + * For Filter: list of filter names. + * + * @type {string[]|undefined} + */ + options: string[] | undefined; } /** @@ -2507,10 +2782,10 @@ export declare class Vector3D { /** * Check for Vector3D equality. * - * @param {object} rhs The other vector to compare to. + * @param {Vector3D} rhs The other vector to compare to. * @returns {boolean} True if both vectors are equal. */ - equals(rhs: object): boolean; + equals(rhs: Vector3D): boolean; /** * Get a string representation of the Vector3D. * @@ -2681,9 +2956,9 @@ export declare class View { /** * Set the default colour map. * - * @param {object} map The colour map. + * @param {ColourMap} map The colour map. */ - setDefaultColourMap(map: object): void; + setDefaultColourMap(map: ColourMap): void; /** * Add window presets to the existing ones. * @@ -2693,16 +2968,16 @@ export declare class View { /** * Get the colour map of the image. * - * @returns {object} The colour map of the image. + * @returns {ColourMap} The colour map of the image. */ - getColourMap(): object; + getColourMap(): ColourMap; /** * Set the colour map of the image. * - * @param {object} map The colour map of the image. + * @param {ColourMap} map The colour map of the image. * @fires View#colourchange */ - setColourMap(map: object): void; + setColourMap(map: ColourMap): void; /** * Get the current position. * @@ -2776,18 +3051,18 @@ export declare class View { * Add an event listener to this class. * * @param {string} type The event type. - * @param {object} callback The method associated with the provided + * @param {Function} callback The function associated with the provided * event type, will be called with the fired event. */ - addEventListener(type: string, callback: object): void; + addEventListener(type: string, callback: Function): void; /** * Remove an event listener from this class. * * @param {string} type The event type. - * @param {object} callback The method associated with the provided + * @param {Function} callback The function associated with the provided * event type. */ - removeEventListener(type: string, callback: object): void; + removeEventListener(type: string, callback: Function): void; /** * Get the image window/level that covers the full data range. * Warning: uses the latest set rescale LUT or the default linear one. @@ -2847,6 +3122,42 @@ export declare class View { #private; } +/** + * View configuration: mainly defines the ´divId´ + * of the associated HTML div. + */ +export declare class ViewConfig { + /** + * @param {string} divId The associated HTML div id. + */ + constructor(divId: string); + /** + * Associated HTML div id. + * + * @type {string} + */ + divId: string; + /** + * Optional orientation of the data; 'axial', 'coronal' or 'sagittal'. + * If undefined, will use the data aquisition plane. + * + * @type {string|undefined} + */ + orientation: string | undefined; + /** + * Optional view colour map. + * + * @type {ColourMap|undefined} + */ + colourMap: ColourMap | undefined; + /** + * Optional layer opacity; in [0, 1] range. + * + * @type {number|undefined} + */ + opacity: number | undefined; +} + /** * View controller. */ @@ -3166,15 +3477,15 @@ export declare class ViewController { /** * Get the colour map. * - * @returns {object} The colour map. + * @returns {ColourMap} The colour map. */ - getColourMap(): object; + getColourMap(): ColourMap; /** * Set the colour map. * - * @param {object} colourMap The colour map. + * @param {ColourMap} colourMap The colour map. */ - setColourMap(colourMap: object): void; + setColourMap(colourMap: ColourMap): void; /** * @callback alphaFn@callback alphaFn * @param {object} value The pixel value. @@ -3197,18 +3508,18 @@ export declare class ViewController { * Add an event listener to this class. * * @param {string} type The event type. - * @param {object} callback The method associated with the provided + * @param {Function} callback The function associated with the provided * event type, will be called with the fired event. */ - addEventListener(type: string, callback: object): void; + addEventListener(type: string, callback: Function): void; /** * Remove an event listener from this class. * * @param {string} type The event type. - * @param {object} callback The method associated with the provided + * @param {Function} callback The function associated with the provided * event type. */ - removeEventListener(type: string, callback: object): void; + removeEventListener(type: string, callback: Function): void; #private; } @@ -3256,12 +3567,14 @@ export declare class ViewLayer { * Handle an image set event. * * @param {object} event The event. + * @function */ onimageset: (event: object) => void; /** * Handle an image change event. * * @param {object} event The event. + * @function */ onimagechange: (event: object) => void; /** @@ -3306,9 +3619,9 @@ export declare class ViewLayer { * Set the layer scale. * * @param {object} newScale The scale as {x,y}. - * @param {Point3D} center The scale center. + * @param {Point3D} [center] The scale center. */ - setScale(newScale: object, center: Point3D): void; + setScale(newScale: object, center?: Point3D): void; /** * Set the base layer offset. Updates the layer offset. * @@ -3411,18 +3724,18 @@ export declare class ViewLayer { * Add an event listener to this class. * * @param {string} type The event type. - * @param {object} callback The method associated with the provided + * @param {Function} callback The function associated with the provided * event type, will be called with the fired event. */ - addEventListener(type: string, callback: object): void; + addEventListener(type: string, callback: Function): void; /** * Remove an event listener from this class. * * @param {string} type The event type. - * @param {object} callback The method associated with the provided + * @param {Function} callback The function associated with the provided * event type. */ - removeEventListener(type: string, callback: object): void; + removeEventListener(type: string, callback: Function): void; /** * Set the current position. * @@ -3567,4 +3880,26 @@ export declare class WindowLut { #private; } +/** + * Writer rule. + */ +export declare class WriterRule { + /** + * @param {string} action The rule action. + */ + constructor(action: string); + /** + * Rule action: `copy`, `remove`, `clear` or `replace`. + * + * @type {string} + */ + action: string; + /** + * Optional value to use for replace action. + * + * @type {any|undefined} + */ + value: any | undefined; +} + export { } diff --git a/lib/tools/dwv/demo/stable/dist/dwv.min.js b/lib/tools/dwv/demo/stable/dist/dwv.min.js index 4ed5eed5..ae6357bb 100644 --- a/lib/tools/dwv/demo/stable/dist/dwv.min.js +++ b/lib/tools/dwv/demo/stable/dist/dwv.min.js @@ -1,2 +1,2 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("konva"),require("jszip"),require("magic-wand-tool")):"function"==typeof define&&define.amd?define(["konva","jszip","konmagic-wand-tool"],t):"object"==typeof exports?exports.dwv=t(require("konva"),require("jszip"),require("magic-wand-tool")):e.dwv=t(e.Konva,e.JSZip,e.MagicWand)}(this,(function(e,t,n){return function(){"use strict";var i={626:function(e){e.exports=t},436:function(t){t.exports=e},812:function(e){e.exports=n}},r={};function o(e){var t=r[e];if(void 0!==t)return t.exports;var n=r[e]={exports:{}};return i[e](n,n.exports,o),n.exports}o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,{a:t}),t},o.d=function(e,t){for(var n in t)o.o(t,n)&&!o.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var a={};return function(){o.r(a),o.d(a,{App:function(){return qn},DicomParser:function(){return fe},DicomWriter:function(){return kn},DrawLayer:function(){return yt},Geometry:function(){return Oe},Image:function(){return He},Index:function(){return e},LayerGroup:function(){return Tt},Matrix33:function(){return C},Point:function(){return O},Point2D:function(){return L},Point3D:function(){return P},RescaleLut:function(){return n},RescaleSlopeAndIntercept:function(){return Ie},Size:function(){return Le},Spacing:function(){return Pe},Tag:function(){return U},TagValueExtractor:function(){return Fe},Vector3D:function(){return f},View:function(){return Xe},ViewController:function(){return Ke},ViewLayer:function(){return it},WindowCenterAndWidth:function(){return r},WindowLut:function(){return s},addTagsToDictionary:function(){return A},buildMultipart:function(){return ie},createImage:function(){return Ge},createMaskImage:function(){return ke},createView:function(){return Ye},customUI:function(){return $e},decoderScripts:function(){return kt},defaultPresets:function(){return i},getDwvVersion:function(){return re},getElementsFromJSONTags:function(){return zn},getOrientationName:function(){return le},getPixelDataTag:function(){return V},getReverseOrientation:function(){return se},getTagFromKey:function(){return Q},getTypedArray:function(){return me},getUID:function(){return Mn},i18n:function(){return on},logger:function(){return h},lut:function(){return S},precisionRound:function(){return K}});class e{#e;constructor(e){if(!e||void 0===e)throw new Error("Cannot create index with no values.");if(0===e.length)throw new Error("Cannot create index with empty values.");if(!e.every((function(e){return!isNaN(e)})))throw new Error("Cannot create index with non number values.");this.#e=e}get(e){return this.#e[e]}length(){return this.#e.length}toString(){return"("+this.#e.toString()+")"}getValues(){return this.#e.slice()}canCompare(e){return!!e&&this.length()===e.length()}equals(e){if(!this.canCompare(e))return!1;for(let t=0,n=this.length();t=this.length())throw new Error("Non valid dimension for toStringId.");let t="";for(let n=0;nr&&(r=i);if(0===r)throw new Error("No dimension found in point stringId");const o=new Array(r);o.fill(0);for(let e=0;ethis.#S?this.#u:e*this.#h+this.#g}equals(e){return null!==e&&this.getCenter()===e.getCenter()&&this.getWidth()===e.getWidth()}toString(){return this.getCenter()+", "+this.getWidth()}}class s{#m;#p;#n=null;#f=null;#i=!1;#D=0;constructor(e,t){this.#m=e,this.#p=t}getWindowLevel(){return this.#f}isSigned(){return this.#p}getRescaleLut(){return this.#m}isReady(){return this.#i}setWindowLevel(e){if(this.#f=e,this.#D=0,this.#f.setSignedOffset(0),this.#p){const e=this.#m.getLength();this.#D=e/2,this.#f.setSignedOffset(this.#m.getRSI().getSlope()*this.#D)}this.#i=!1}update(){if(this.#i)return;this.#m.isReady()||this.#m.initialise();const e=this.#m.getLength();this.#n||(this.#n=new Uint8ClampedArray(e));for(let t=0;t255?255:t})),green:c((function(e){const t=l/3;let n=0;return e>=t&&(n=3*(e-t),n>255)?255:n})),blue:c((function(e){const t=l/3;let n=0;return e>=2*t&&(n=3*(e-2*t),n>255)?255:n}))},hot_iron:{red:[0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,220,222,224,226,228,230,232,234,236,238,240,242,244,246,248,250,252,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255],green:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,220,222,224,226,228,230,232,234,236,238,240,242,244,246,248,250,252,255],blue:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,140,144,148,152,156,160,164,168,172,176,180,184,188,192,196,200,204,208,212,216,220,224,228,232,236,240,244,248,252,255]},pet:{red:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,77,79,81,83,85,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,171,173,175,177,179,181,183,185,187,189,191,193,195,197,199,201,203,205,207,209,211,213,215,217,219,221,223,225,227,229,231,233,235,237,239,241,243,245,247,249,251,253,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255],green:[0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,65,67,69,71,73,75,77,79,81,83,85,87,89,91,93,95,97,99,101,103,105,107,109,111,113,115,117,119,121,123,125,128,126,124,122,120,118,116,114,112,110,108,106,104,102,100,98,96,94,92,90,88,86,84,82,80,78,76,74,72,70,68,66,64,63,61,59,57,55,53,51,49,47,45,43,41,39,37,35,33,31,29,27,25,23,21,19,17,15,13,11,9,7,5,3,1,0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198,200,202,204,206,208,210,212,214,216,218,220,222,224,226,228,230,232,234,236,238,240,242,244,246,248,250,252,255],blue:[0,1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,77,79,81,83,85,87,89,91,93,95,97,99,101,103,105,107,109,111,113,115,117,119,121,123,125,127,129,131,133,135,137,139,141,143,145,147,149,151,153,155,157,159,161,163,165,167,169,171,173,175,177,179,181,183,185,187,189,191,193,195,197,199,201,203,205,207,209,211,213,215,217,219,221,223,225,227,229,231,233,235,237,239,241,243,245,247,249,251,253,255,252,248,244,240,236,232,228,224,220,216,212,208,204,200,196,192,188,184,180,176,172,168,164,160,156,152,148,144,140,136,132,128,124,120,116,112,108,104,100,96,92,88,84,80,76,72,68,64,60,56,52,48,44,40,36,32,28,24,20,16,12,8,4,0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,85,89,93,97,101,105,109,113,117,121,125,129,133,137,141,145,149,153,157,161,165,170,174,178,182,186,190,194,198,202,206,210,214,218,222,226,230,234,238,242,246,250,255]},hot_metal_blue:{red:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,6,9,12,15,18,21,24,26,29,32,35,38,41,44,47,50,52,55,57,59,62,64,66,69,71,74,76,78,81,83,85,88,90,93,96,99,102,105,108,111,114,116,119,122,125,128,131,134,137,140,143,146,149,152,155,158,161,164,166,169,172,175,178,181,184,187,190,194,198,201,205,209,213,217,221,224,228,232,236,240,244,247,251,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255],green:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,9,11,13,15,17,19,21,23,24,26,28,30,32,34,36,38,40,41,43,45,47,49,51,53,55,56,58,60,62,64,66,68,70,72,73,75,77,79,81,83,85,87,88,90,92,94,96,98,100,102,104,105,107,109,111,113,115,117,119,120,122,124,126,128,130,132,134,136,137,139,141,143,145,147,149,151,152,154,156,158,160,162,164,166,168,169,171,173,175,177,179,181,183,184,186,188,190,192,194,196,198,200,201,203,205,207,209,211,213,215,216,218,220,222,224,226,228,229,231,233,235,237,239,240,242,244,246,248,250,251,253,255],blue:[0,2,4,6,8,10,12,14,16,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,77,79,81,83,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,117,119,121,123,125,127,129,131,133,135,137,139,141,143,145,147,149,151,153,155,157,159,161,163,165,167,169,171,173,175,177,179,181,183,184,186,188,190,192,194,196,198,200,197,194,191,188,185,182,179,176,174,171,168,165,162,159,156,153,150,144,138,132,126,121,115,109,103,97,91,85,79,74,68,62,56,50,47,44,41,38,35,32,29,26,24,21,18,15,12,9,6,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,6,9,12,15,18,21,24,26,29,32,35,38,41,44,47,50,53,56,59,62,65,68,71,74,76,79,82,85,88,91,94,97,100,103,106,109,112,115,118,121,124,126,129,132,135,138,141,144,147,150,153,156,159,162,165,168,171,174,176,179,182,185,188,191,194,197,200,203,206,210,213,216,219,223,226,229,232,236,239,242,245,249,252,255]},pet_20step:{red:[0,0,0,0,0,0,0,0,0,0,0,0,0,96,96,96,96,96,96,96,96,96,96,96,96,96,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,80,80,80,80,80,80,80,80,80,80,80,80,80,96,96,96,96,96,96,96,96,96,96,96,96,96,112,112,112,112,112,112,112,112,112,112,112,112,112,128,128,128,128,128,128,128,128,128,128,128,128,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,80,80,80,80,80,80,80,80,80,80,80,80,80,64,64,64,64,64,64,64,64,64,64,64,64,224,224,224,224,224,224,224,224,224,224,224,224,224,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,208,192,192,192,192,192,192,192,192,192,192,192,192,192,176,176,176,176,176,176,176,176,176,176,176,176,176,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255],green:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,80,80,80,80,80,80,80,80,80,80,80,80,80,96,96,96,96,96,96,96,96,96,96,96,96,96,112,112,112,112,112,112,112,112,112,112,112,112,112,128,128,128,128,128,128,128,128,128,128,128,128,96,96,96,96,96,96,96,96,96,96,96,96,96,144,144,144,144,144,144,144,144,144,144,144,144,144,192,192,192,192,192,192,192,192,192,192,192,192,192,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,208,208,208,208,208,208,208,208,208,208,208,208,208,176,176,176,176,176,176,176,176,176,176,176,176,176,144,144,144,144,144,144,144,144,144,144,144,144,96,96,96,96,96,96,96,96,96,96,96,96,96,48,48,48,48,48,48,48,48,48,48,48,48,48,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255],blue:[0,0,0,0,0,0,0,0,0,0,0,0,0,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,112,112,112,112,112,112,112,112,112,112,112,112,128,128,128,128,128,128,128,128,128,128,128,128,128,176,176,176,176,176,176,176,176,176,176,176,176,176,192,192,192,192,192,192,192,192,192,192,192,192,192,224,224,224,224,224,224,224,224,224,224,224,224,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,80,80,80,80,80,80,80,80,80,80,80,80,80,64,64,64,64,64,64,64,64,64,64,64,64,80,80,80,80,80,80,80,80,80,80,80,80,80,96,96,96,96,96,96,96,96,96,96,96,96,96,64,64,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255]}},h={levels:{TRACE:0,DEBUG:1,INFO:2,WARN:3,ERROR:4},level:3,trace:function(e){this.level<=this.levels.TRACE&&console.trace(e)},debug:function(e){this.level<=this.levels.DEBUG&&console.debug(e)},info:function(e){this.level<=this.levels.INFO&&console.info(e)},warn:function(e){this.level<=this.levels.WARN&&console.warn(e)},error:function(e){this.level<=this.levels.ERROR&&console.error(e)}};function g(e){return function(e){return n=e,.001172549*(t={r:parseInt(n.substring(1,3),16),g:parseInt(n.substring(3,5),16),b:parseInt(n.substring(5,7),16)}).r+.002301961*t.g+447059e-9*t.b<.5;var t,n}(e)?"#fff":"#000"}const m={x:95.0489,y:100,z:108.884};function p(e){const t={Yellow:"#ffff00",Red:"#ff0000",White:"#ffffff",Green:"#008000",Blue:"#0000ff",Lime:"#00ff00",Fuchsia:"#ff00ff",Black:"#000000"};let n="#ffff00";return void 0!==t[e]&&(n=t[e]),n}class f{#y;#C;#v;constructor(e,t,n){this.#y=e,this.#C=t,this.#v=n}getX(){return this.#y}getY(){return this.#C}getZ(){return this.#v}equals(e){return null!==e&&this.getX()===e.getX()&&this.getY()===e.getY()&&this.getZ()===e.getZ()}toString(){return"("+this.getX()+", "+this.getY()+", "+this.getZ()+")"}norm(){return Math.sqrt(this.getX()*this.getX()+this.getY()*this.getY()+this.getZ()*this.getZ())}crossProduct(e){return new f(this.getY()*e.getZ()-e.getY()*this.getZ(),this.getZ()*e.getX()-e.getZ()*this.getX(),this.getX()*e.getY()-e.getX()*this.getY())}dotProduct(e){return this.getX()*e.getX()+this.getY()*e.getY()+this.getZ()*e.getZ()}}const D=1e4*Number.EPSILON;function y(e,t,n){return void 0===n&&(n=Number.EPSILON),Math.abs(e-t)0?1:-1;for(let t=0;t<3;++t)t===n.index?e.push(1*i):e.push(0)}return new C(e)}getThirdColMajorDirection(){return this.getColAbsMax(2).index}}function v(){return new C([1,0,0,0,1,0,0,0,1])}function T(){return new C([1,0,0,0,0,1,0,-1,0])}function I(e){let t=null;return"axial"===e?t=v():"coronal"===e?t=T():"sagittal"===e&&(t=new C([0,0,-1,1,0,0,0,-1,0])),t}class L{#y;#C;constructor(e,t){this.#y=e,this.#C=t}getX(){return this.#y}getY(){return this.#C}equals(e){return null!==e&&this.getX()===e.getX()&&this.getY()===e.getY()}toString(){return"("+this.getX()+", "+this.getY()+")"}getDistance(e){return Math.sqrt((this.getX()-e.getX())*(this.getX()-e.getX())+(this.getY()-e.getY())*(this.getY()-e.getY()))}getRound(){return new L(Math.round(this.getX()),Math.round(this.getY()))}}class P{#y;#C;#v;constructor(e,t,n){this.#y=e,this.#C=t,this.#v=n}getX(){return this.#y}getY(){return this.#C}getZ(){return this.#v}equals(e){return null!==e&&this.getX()===e.getX()&&this.getY()===e.getY()&&this.getZ()===e.getZ()}isSimilar(e,t){return null!==e&&y(this.getX(),e.getX(),t)&&y(this.getY(),e.getY(),t)&&y(this.getZ(),e.getZ(),t)}toString(){return"("+this.getX()+", "+this.getY()+", "+this.getZ()+")"}getDistance(e){return Math.sqrt((this.getX()-e.getX())*(this.getX()-e.getX())+(this.getY()-e.getY())*(this.getY()-e.getY())+(this.getZ()-e.getZ())*(this.getZ()-e.getZ()))}minus(e){return new f(this.getX()-e.getX(),this.getY()-e.getY(),this.getZ()-e.getZ())}}class O{#e;constructor(e){if(!e||void 0===e)throw new Error("Cannot create point with no values.");if(0===e.length)throw new Error("Cannot create point with empty values.");if(!e.every((function(e){return!isNaN(e)})))throw new Error("Cannot create point with non number values.");this.#e=e}get(e){return this.#e[e]}length(){return this.#e.length}toString(){return"("+this.#e.toString()+")"}getValues(){return this.#e.slice()}canCompare(e){return!!e&&this.length()===e.length()}equals(e){if(!this.canCompare(e))return!1;for(let t=0,n=this.length();to;t--,o++)r=n[o],n[o]=n[t],n[t]=r}class W{#P;#O=!0;#w=function(){return new Int8Array(new Int16Array([1]).buffer)[0]>0}();#A;#x;constructor(e,t){this.#P=e,void 0!==t&&(this.#O=t),this.#A=this.#O!==this.#w,this.#x=new DataView(e)}readUint16(e){return this.#x.getUint16(e,this.#O)}readInt16(e){return this.#x.getInt16(e,this.#O)}readUint32(e){return this.#x.getUint32(e,this.#O)}readBigUint64(e){return this.#x.getBigUint64(e,this.#O)}readInt32(e){return this.#x.getInt32(e,this.#O)}readBigInt64(e){return this.#x.getBigInt64(e,this.#O)}readFloat32(e){return this.#x.getFloat32(e,this.#O)}readFloat64(e){return this.#x.getFloat64(e,this.#O)}readBinaryArray(e,t){const n=new Uint8Array(this.#P,e,t),i=8*n.length,r=new Uint8Array(i);let o=0,a=0;for(let e=0;e0?0|n:0;return e.substring(i,i+t.length)===t}function Y(e,t){return null!=e&&null!=t&&e.substring(e.length-t.length)===t}function X(e){const t=[];if(null==e)return t;const n=/{(\w+)}/g;let i=n.exec(e);for(;i;)t.push(i[1]),i=n.exec(e);return t}function j(e,t){let n="";if(null==e)return n;if(n=e,null==t)return n;const i=X(e);for(let e=0;e=e.length)&&(n=0),(void 0===i||i<=n||i>e.length)&&(i=e.length);for(let r=n;r2^"+e+").")}}return i}function pe(e,t){return t?8:F(e)?12:8}class fe{#b={};#F;#R=new ae;#E=this.#R;#q(e){return this.#R.decode(e)}#U(e){return this.#E.decode(e)}getDefaultCharacterSet(){return this.#F}setDefaultCharacterSet(e){this.#F=e}setDecoderCharacterSet(e){this.#E=new TextDecoder(e)}getDicomElements(){return this.#b}#Q(e,t){const n=e.readHex(t);t+=Uint16Array.BYTES_PER_ELEMENT;const i=e.readHex(t);return t+=Uint16Array.BYTES_PER_ELEMENT,{tag:new U(n,i),endOffset:t}}#M(e,t,n){const i={};let r=this.#B(e,t,n);if(t=r.endOffset,N(r.tag))return{data:i,endOffset:r.endOffset,isSeqDelim:!0};if(i[r.tag.getKey()]={tag:r.tag,vr:"NONE",vl:r.vl,undefinedLength:r.undefinedLength},r.undefinedLength){let o=!1;for(;!o;)r=this.#B(e,t,n),t=r.endOffset,o="FFFEE00D"===r.tag.getKey(),o||(i[r.tag.getKey()]=r)}else{const o=t;for(t-=r.vl;t8&&"OB"===a&&h.warn("Reading DICOM pixel data with bitsAllocated>8 and OB VR."),l=[],1===i)l.push(t.readBinaryArray(s,o));else if(8===i)0===n?l.push(t.readUint8Array(s,o)):l.push(t.readInt8Array(s,o));else{if(16!==i)throw new Error("Unsupported bits allocated: "+i);0===n?l.push(t.readUint16Array(s,o)):l.push(t.readInt16Array(s,o))}else if(void 0!==c)if("Uint8"===c)l=t.readUint8Array(s,o);else if("Uint16"===c)l=t.readUint16Array(s,o),"O"!==a[0]&&(l=Array.from(l));else if("Uint32"===c)l=t.readUint32Array(s,o),"O"!==a[0]&&(l=Array.from(l));else if("Uint64"===c)l=t.readUint64Array(s,o);else if("Int16"===c)l=Array.from(t.readInt16Array(s,o));else if("Int32"===c)l=Array.from(t.readInt32Array(s,o));else if("Int64"===c)l=t.readInt64Array(s,o);else if("Float32"===c)l=Array.from(t.readFloat32Array(s,o));else if("Float64"===c)l=Array.from(t.readFloat64Array(s,o));else{if("string"!==c)throw Error("Unknown VR type: "+c);{const e=t.readUint8Array(s,o);l=E(a)?this.#U(e):this.#q(e),l=function(e){let t=e;const n=e.length-1;return e[n]===oe&&(t=e.substring(0,n)),t=t.trim(),t}(l).split("\\")}}else if("xx"===a)l=Array.from(t.readUint16Array(s,o));else if("ox"===a)l=8===i?Array.from(t.readUint8Array(s,o)):Array.from(t.readUint16Array(s,o));else if("xs"===a)l=0===n?Array.from(t.readUint16Array(s,o)):Array.from(t.readInt16Array(s,o));else if("AT"===a){const e=t.readUint16Array(s,o);l=[];for(let t=0,n=e.length;t=65&&r<=90&&o>=65&&o<=90);let s=null;if(n===t)s=a?"1.2.840.10008.1.2":"1.2.840.10008.1.2.1";else{if(a)throw new Error("Not a valid DICOM file (no magic DICM word foundand implicit VR big endian detected)");s="1.2.840.10008.1.2.2"}const l={tag:new U("0002","0010"),vr:"UI"};return l.value=[s+" "],l.vl=l.value[0].length,l.startOffset=e.startOffset,l.endOffset=l.startOffset+l.vl,l}(i);this.#b[e.tag.getKey()]=e,n=e.value[0],t=0}if(!function(e){return"1.2.840.10008.1.2"===e||"1.2.840.10008.1.2.1"===e||"1.2.840.10008.1.2.2"===e||de(e)||Se(e)||he(e)||ge(e)}(n))throw new Error("Unsupported DICOM transfer syntax: '"+n+"' ("+function(e){let t="Unknown";return"1.2.840.10008.1.2"===e?t="Little Endian Implicit":"1.2.840.10008.1.2.1"===e?t="Little Endian Explicit":"1.2.840.10008.1.2.1.99"===e?t="Little Endian Deflated Explicit":"1.2.840.10008.1.2.2"===e?t="Big Endian Explicit":de(e)?t="1.2.840.10008.1.2.4.50"===e?"JPEG Baseline":"JPEG Extended, Process 2+4":Se(e)?t="1.2.840.10008.1.2.4.57"===e?"JPEG Lossless, Nonhierarchical (Processes 14)":"JPEG Lossless, Non-hierarchical, 1st Order Prediction":function(e){return null!==e.match(/1.2.840.10008.1.2.4.5/)&&!de(e)&&!Se(e)||null!==e.match(/1.2.840.10008.1.2.4.6/)}(e)?t="Retired JPEG":function(e){return null!==e.match(/1.2.840.10008.1.2.4.8/)}(e)?t="JPEG-LS":he(e)?t="1.2.840.10008.1.2.4.91"===e?"JPEG 2000 (Lossless or Lossy)":"JPEG 2000 (Lossless only)":"1.2.840.10008.1.2.4.100"===e?t="MPEG2":ge(e)&&(t="RLE"),t}(n)+")");let s=!1;for(ce(n)&&(s=!0),ue(n)&&(o=new W(e,!1));t1&&t.length>e){const n=t.length/e,r=[];let o=0;for(let i=0;i{if(void 0===this.#k[e.type])return;const t=this.#k[e.type].slice();for(let n=0;n2?e:0})));let c=o.indexToOffset(l);void 0===i&&(i=!1);let u=null;u=i?function(e){return t.getRescaledValueAtOffset(e)}:function(e){return t.getValueAtOffset(e)};const d=o.get(0),S=o.get(1),h=o.get(2);let g=o.getDimSize(2);const m=t.getNumberOfComponents(),p=1===t.getPlanarConfiguration(),f=function(e,t,n,i,r,o,a,s){return 1===m?ye(e,t,n,i,r,o,a,s):3===m?function(e,t,n,i,r,o,a,s,l){const c=[];return l?(c.push(ye(e,t,n,i,r,o,a,s)),c.push(ye(e,t+n*i,n,i,r,o,a,s)),c.push(ye(e,t+2*n*i,n,i,r,o,a,s))):(i*=3,o*=3,c.push(ye(e,t,n,i,r,o,a,s)),c.push(ye(e,t+1,n,i,r,o,a,s)),c.push(ye(e,t+2,n,i,r,o,a,s))),{next:function(){const e=c[0].next(),t=c[1].next(),n=c[2].next();return e.done?{done:!0,index:n.index}:{value:[e.value,t.value,n.value],done:!1,index:[e.index,t.index,n.index]}}}}(e,3*t,n,i,r,o,a,s,p):void 0};let D=null;if(r&&void 0!==r){const e=r.getColAbsMax(0),t=r.getColAbsMax(2),n=!1,i=!1;let o=null;if(2===t.index)o=d*S,D=0===e.index?f(u,c,o,1,d,d,n,i):f(u,c,o,d,S,1,n,i);else if(0===t.index)o=h*S,D=1===e.index?f(u,c,o,d,S,g,n,i):f(u,c,o,g,h,d,n,i);else{if(1!==t.index)throw new Error("Unknown direction: "+t.index);o=h*d,D=0===e.index?f(u,c,o,1,d,g,n,i):f(u,c,o,g,h,1,n,i)}}else if(1===t.getNumberOfComponents())D=function(e,t,n,i){void 0===i&&(i=1);let r=t;return{next:function(){if(r=e[i+1].index&&++i;const t={value:e[i].colour,done:!1,index:n};return++n,t}return{done:!0,index:t}}}}class Ie{#h;#H;constructor(e,t){this.#h=e,this.#H=t}getSlope(){return this.#h}getIntercept(){return this.#H}apply(e){return e*this.#h+this.#H}equals(e){return null!==e&&this.getSlope()===e.getSlope()&&this.getIntercept()===e.getIntercept()}toString(){return this.getSlope()+", "+this.getIntercept()}isID(){return 1===this.getSlope()&&0===this.getIntercept()}}class Le{#e;constructor(e){if(!e||void 0===e)throw new Error("Cannot create size with no values.");if(0===e.length)throw new Error("Cannot create size with empty values.");if(!e.every((function(e){return!isNaN(e)&&0!==e})))throw new Error("Cannot create size with non number or zero values.");this.#e=e}get(e){return this.#e[e]}length(){return this.#e.length}toString(){return"("+this.#e.toString()+")"}getValues(){return this.#e.slice()}moreThanOne(e){return this.length()>=e+1&&1!==this.get(e)}canScroll3D(e){let t=2;return void 0!==e&&(t=e.getThirdColMajorDirection()),this.moreThanOne(t)}canScroll(e){let t=this.canScroll3D(e);for(let e=3;ethis.length())return null;if(void 0===t)t=0;else if(t<0||t>e)throw new Error("Invalid start value for getDimSize");let n=1;for(let i=t;in-1)throw new Error("Wrong input dir value: "+t[e]);for(let n=0;n=0&&ithis.length()-1)throw new Error("Invalid start value for indexToOffset");let n=0;for(let i=t;i0;--e)r=this.getDimSize(e),n[e]=Math.floor(i/r),i-=n[e]*r;return n[0]=i,new e(n)}get2D(){return{x:this.get(0),y:this.get(1)}}}class Pe{#e;constructor(e){if(!e||void 0===e)throw new Error("Cannot create spacing with no values.");if(0===e.length)throw new Error("Cannot create spacing with empty values.");if(!e.every((function(e){return!isNaN(e)&&0!==e})))throw new Error("Cannot create spacing with non number or zero values.");this.#e=e}get(e){return this.#e[e]}length(){return this.#e.length}toString(){return"("+this.#e.toString()+")"}getValues(){return this.#e.slice()}equals(e){if(!e)return!1;const t=this.length();if(t!==e.length())return!1;for(let n=0;n0?i+1:i}appendOrigin(e,t,n){if(void 0!==n&&this.#X[n].splice(t,0,e),void 0===n||n===this.#j){this.#Z=!0,this.#W.splice(t,0,e);const n=this.#z.getValues();n[2]+=1,this.#z=new Le(n)}}appendFrame(e,t){this.#X[t]=[e];const n=this.#z.getValues(),i=this.#Y.getValues();4===n.length?n[3]+=1:(n.push(2),i.push(1)),this.#z=new Le(n),this.#Y=new Pe(i)}toString(){return"Origin: "+this.getOrigin()+", Size: "+this.getSize()+", Spacing: "+this.getSpacing()+", Orientation: "+this.getOrientation()}equals(e){return null!==e&&this.getOrigin().equals(e.getOrigin())&&this.getSize().equals(e.getSize())&&this.getSpacing().equals(e.getSpacing())}isInBounds(e){return this.isIndexInBounds(this.worldToIndex(e))}isIndexInBounds(e,t){return this.getSize().isInBounds(e,t)}indexToWorld(e){const t=this.getSpacing(),n=new P(e.get(0)*t.get(0),e.get(1)*t.get(1),e.get(2)*t.get(2)),i=this.getOrientation().multiplyPoint3D(n),r=e.getValues(),o=this.getOrigin();return r[0]=o.getX()+i.getX(),r[1]=o.getY()+i.getY(),r[2]=o.getZ()+i.getZ(),new O(r)}pointToWorld(e){const t=this.getSpacing(),n=new P(e.getX()*t.get(0),e.getY()*t.get(1),e.getZ()*t.get(2)),i=this.getOrientation().multiplyPoint3D(n),r=this.getOrigin();return new P(r.getX()+i.getX(),r.getY()+i.getY(),r.getZ()+i.getZ())}worldToIndex(t){const n=this.getOrigin(),i=new P(t.get(0)-n.getX(),t.get(1)-n.getY(),t.get(2)-n.getZ()),r=this.getOrientation().getInverse().multiplyPoint3D(i),o=t.getValues(),a=this.getSpacing();return o[0]=Math.round(r.getX()/a.get(0)),o[1]=Math.round(r.getY()/a.get(1)),o[2]=Math.round(r.getZ()/a.get(2)),new e(o)}worldToPoint(e){const t=this.getOrigin(),n=new P(e.get(0)-t.getX(),e.get(1)-t.getY(),e.get(2)-t.getZ()),i=this.getOrientation().getInverse().multiplyPoint3D(n),r=e.getValues(),o=this.getSpacing();return r[0]=i.getX()/o.get(0),r[1]=i.getY()/o.get(1),r[2]=i.getZ()/o.get(2),new P(r[0],r[1],r[2])}}function we(e,t){return t.getInverse().multiplyArray3D(e)}function Ae(e,t){return t.multiplyArray3D(e)}function xe(e,t,n){if(void 0===n&&(n=!0),e.length<=1)return;const i=t.getInverse();let r=i.multiplyVector3D(e[0]),o=i.multiplyVector3D(e[1]),a=Math.abs(r.getZ()-o.getZ());const s=[];for(let t=0;t1e-4&&h.warn("Varying slice spacing, mean delta: "+t.toFixed(3)+" ("+s.length+" case(s))")}return a}function be(e){const t=e["00280010"];if(void 0===t)throw new Error("Missing DICOM image number of rows");if(0===t.value.length)throw new Error("Empty DICOM image number of rows");const n=e["00280011"];if(void 0===n)throw new Error("Missing DICOM image number of columns");if(0===n.value.length)throw new Error("Empty DICOM image number of columns");return[n.value[0],t.value[0]]}class Fe{getTime(e){}}class Re{checkElements(e){be(e)}create(e,t,n){const i=be(e),o=[i[0],i[1],1],a=e["00280008"];a&&o.push(a.value[0]);const s=new Le(o),l=function(e){let t=1,n=1;const i=["00280030","00181164","00182010","00280034"];for(let r=0;r>8};t=t.map(e),n=n.map(e),i=i.map(e)}}else if(8===r[2]){h.info("Scaling 16bits color lut since the lut descriptor is 8.");let e=t.slice(0);t=new Uint8Array(e.buffer),e=n.slice(0),n=new Uint8Array(e.buffer),e=i.slice(0),i=new Uint8Array(e.buffer)}M.paletteLut={red:t,green:n,blue:i}}const Z=e["00082144"];return void 0!==Z&&(M.RecommendedDisplayFrameRate=parseInt(Z.value[0],10)),x.setMeta(M),x}}function Ee(e,t){return JSON.stringify(e)===JSON.stringify(t)}function qe(e,t){const n=e[t.tag];if(1===t.type||2===t.type){if(void 0===n)throw new Error("Missing or empty "+t.name)}else if(void 0===n)return;let i,r=!1;if(i=1===n.value.length?n.value[0]:n.value,Array.isArray(i))for(let e=0;e.206896552?Math.pow(e,3):.128418549*e-.017712903,t}const n=m,i=(e.l+16)/116;return{x:n.x*t(i+e.a/500),y:n.y*t(i),z:n.z*t(i-e.b/200)}}(e))}({l:.001525902*(n={l:i[0],a:i[1],b:i[2]}).l,a:.003891051*n.a-128,b:.003891051*n.b-128});t.displayValue=r}var n;if(void 0===e["00620003"])throw Error("Missing Segmented Property Category Code Sequence.");if(t.propertyCategoryCode=Qe(e["00620003"].value[0]),void 0===e["0062000F"])throw Error("Missing Segmented Property Type Code Sequence.");return t.propertyTypeCode=Qe(e["0062000F"].value[0]),void 0!==e["00620020"]&&(t.trackingId=e["00620020"].value[0],t.trackingUid=e["00620021"].value[0]),t}function Be(e){if(void 0===e["00280030"])return null;const t=e["00280030"],n=[parseFloat(t.value[0]),parseFloat(t.value[1])];return void 0!==e["00180050"]?n.push(parseFloat(e["00180050"].value[0])):void 0!==e["00180088"]&&n.push(parseFloat(e["00180088"].value[0])),new Pe(n)}function Ne(e){const t=[];if(void 0!==e["00089124"]){const n=e["00089124"].value;for(let e=0;e1e-4;return t&&(t=e>.001,t?(t=e>.01,t||h.warn("Using larger+ real world epsilon in SEG pos pat adding")):h.warn("Using larger real world epsilon in SEG pos pat adding")),t},U=[];U.push(T[0]);let Q=0;for(let t=1;ta)throw new Error("Test distance is increasing when adding intermediate pos pats");U.push(T[t])}const M=U.length,B=new Oe(x[0],r,b,w),N=[0];for(let e=1;e=this.#ae.numberOfFiles?h.warn("Ignoring frame at index "+t+" (size: "+this.#ae.numberOfFiles+")"):(this.#P.set(e,i*t),this.appendFrame(t,new P(0,0,0)))}appendFrame(e,t){this.#J.appendFrame(t,e),this.#ge({type:"appendframe"})}getDataRange(){return this.#se||(this.#se=this.calculateDataRange()),this.#se}getRescaledDataRange(){return this.#le||(this.#le=this.calculateRescaledDataRange()),this.#le}getHistogram(){if(!this.#ce){const e=this.calculateHistogram();this.#se=e.dataRange,this.#le=e.rescaledDataRange,this.#ce=e.histogram}return this.#ce}addEventListener(e,t){this.#ue.add(e,t)}removeEventListener(e,t){this.#ue.remove(e,t)}#ge=e=>{this.#ue.fireEvent(e)};setAtOffsets(e,t){let n;for(let i=0,r=e.length;i=3&&(r=i.getDimSize(3));for(let i=0;it&&(t=n),nn?t:n}}{let e=this.getRescaledValueAtOffset(0),t=e,n=0;const i=this.getGeometry().getSize();let r=i.getTotalSize();3===i.length()&&(r=i.getDimSize(3));for(let i=0;it&&(t=n),ni&&(i=r),ra&&(a=s),s{const t=this.getCurrentIndex();if(3===t.length()){const n=t.getValues();n.push(0),this.setCurrentIndex(new e(n))}}))}getImage(){return this.#me}setImage(e){this.#me=e}getOrientation(){return this.#_}setOrientation(e){this.#_=e}init(){this.setInitialIndex()}setInitialIndex(){const t=this.#me.getGeometry().getSize(),n=new Array(t.length());n.fill(0),n[0]=Math.floor(t.get(0)/2),n[1]=Math.floor(t.get(1)/2),n[2]=Math.floor(t.get(2)/2),this.setCurrentIndex(new e(n),!0)}getPlaybackMilliseconds(e){return e||(e=10),Math.round(1e3/e)}#Te=function(e,t){return 255};getAlphaFunction(){return this.#Te}setAlphaFunction(e){this.#Te=e,this.#ge({type:"alphafuncchange"})}getCurrentWindowLut(e){this.getCurrentIndex()||this.setInitialIndex();const t=this.getCurrentIndex();void 0===e&&(e=this.#me.getRescaleSlopeAndIntercept(t));let i=null;if(this.#De&&void 0!==this.#fe[this.#De]&&void 0!==this.#fe[this.#De].perslice&&!0===this.#fe[this.#De].perslice){const e=this.#me.getSecondaryOffset(t);i=this.#fe[this.#De].wl[e]}i||(this.#ye||this.setWindowLevelPresetById(0,!0),i=this.#ye);let r=this.#pe[e.toString()];if(void 0===r){const e=new n(this.#me.getRescaleSlopeAndIntercept(),this.#me.getMeta().BitsStored),t=new s(e,this.#me.getMeta().IsSigned);this.addWindowLut(t),r=t}const o=r.getWindowLevel();return i.equals(o)||(r.setWindowLevel(i),r.update(),o&&o.getWidth()===i.getWidth()&&o.getCenter()===i.getCenter()||this.#ge({type:"wlchange",value:[i.getCenter(),i.getWidth()],wc:i.getCenter(),ww:i.getWidth(),skipGenerate:!0})),r}addWindowLut(e){const t=e.getRescaleLut().getRSI();this.#pe[t.toString()]=e}getWindowPresets(){return this.#fe}getWindowPresetsNames(){return Object.keys(this.#fe)}setWindowPresets(e){this.#fe=e}setDefaultColourMap(e){this.#Ce=e}addWindowPresets(e){const t=Object.keys(e);let n=null;for(let i=0;i{this.#ue.fireEvent(e)};getWindowLevelMinMax(){const e=this.getImage().getRescaledDataRange(),t=e.min;let n=e.max-t;return n<1&&(h.warn("Zero or negative window width, defaulting to one."),n=1),new r(t+n/2,n)}setWindowLevelMinMax(){const e=this.getWindowLevelMinMax();this.setWindowLevel(e.getCenter(),e.getWidth(),"minmax")}generateImageData(e,t){void 0===t&&(this.getCurrentIndex()||this.setInitialIndex(),t=this.getCurrentIndex());const n=this.getImage(),i=ve(n,t,!1,this.getOrientation()),r=n.getPhotometricInterpretation();switch(r){case"MONOCHROME1":case"MONOCHROME2":!function(e,t,n,i,r){let o=0,a=0,s=t.next();for(;!s.done;)a=i.getValue(s.value),e.data[o]=r.red[a],e.data[o+1]=r.green[a],e.data[o+2]=r.blue[a],e.data[o+3]=n(s.value,s.index),o+=4,s=t.next()}(e,i,this.getAlphaFunction(),this.getCurrentWindowLut(),this.getColourMap());break;case"PALETTE COLOR":!function(e,t,n,i,r){const o=function(e){return e>>8};r&&h.info("Scaling 16bits data to 8bits.");let a=0,s=0,l=t.next();for(;!l.done;)s=l.value,r?(e.data[a]=o(i.red[s]),e.data[a+1]=o(i.green[s]),e.data[a+2]=o(i.blue[s])):(e.data[a]=i.red[s],e.data[a+1]=i.green[s],e.data[a+2]=i.blue[s]),e.data[a+3]=n(s,l.index),a+=4,l=t.next()}(e,i,this.getAlphaFunction(),this.getColourMap(),16===n.getMeta().BitsStored);break;case"RGB":!function(e,t,n){let i=0,r=t.next();for(;!r.done;)e.data[i]=r.value[0],e.data[i+1]=r.value[1],e.data[i+2]=r.value[2],e.data[i+3]=n(r.value,r.index),i+=4,r=t.next()}(e,i,this.getAlphaFunction());break;case"YBR_FULL":!function(e,t,n){let i=0,r=null,o=t.next();for(;!o.done;)a=o.value[0],s=o.value[1],r={r:a+1.402*((l=o.value[2])-128),g:a-.34414*(s-128)-.71414*(l-128),b:a+1.772*(s-128)},e.data[i]=r.r,e.data[i+1]=r.g,e.data[i+2]=r.b,e.data[i+3]=n(o.value,o.index),i+=4,o=t.next();var a,s,l}(e,i,this.getAlphaFunction());break;default:throw new Error("Unsupported photometric interpretation: "+r)}}incrementIndex(t,n){const i=this.getCurrentIndex(),r=new Array(i.length());r.fill(0),t