All files / Sources/Rendering/Misc/TextureLODsDownloader index.js

17.18% Statements 11/64
0% Branches 0/27
15.38% Functions 2/13
17.18% Lines 11/64

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186    1x       1x                                                             1x   1x           1x                                                                                                                                                                                           1x                               1x   1x     1x                       1x         1x                
import macro from 'vtk.js/Sources/macros';
 
const { vtkErrorMacro } = macro;
 
// ----------------------------------------------------------------------------
 
const getRemoteFileSize = (url) =>
  // This function only works if the server provides a 'Content-Length'.
  // For some reason, the 'Content-Length' header does not appear to be
  // given for CORS HEAD requests on firefox. So this will not work on
  // firefox if the images do not have the same origin as the html file.
  // TODO: figure out how to make this work for CORS requests on firefox.
  new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('HEAD', url, true);
 
    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          const size = xhr.getResponseHeader('Content-Length');
          resolve(size);
        } else {
          console.log('Failed to get remote file size of', url);
          reject(xhr);
        }
      }
    };
 
    xhr.send();
  });
 
// ----------------------------------------------------------------------------
// vtkTextureLODsDownloader methods
// ----------------------------------------------------------------------------
 
function vtkTextureLODsDownloader(publicAPI, model) {
  // Set our className
  model.classHierarchy.push('vtkTextureLODsDownloader');
 
  const internal = {
    downloadStack: [],
  };
 
  //--------------------------------------------------------------------------
 
  publicAPI.startDownloads = () => {
    if (!model.texture) {
      vtkErrorMacro('Texture was not set.');
      return;
    }
 
    if (!model.files || model.files.length === 0) {
      vtkErrorMacro('No files set.');
      return;
    }
 
    let baseUrl = model.baseUrl;
    if (baseUrl && !baseUrl.endsWith('/')) {
      baseUrl += '/';
    }
 
    // Create the download stack
    internal.downloadStack = [];
    model.files.forEach((file) =>
      internal.downloadStack.push(`${baseUrl}${file}`)
    );
 
    const downloadNextTexture = () => {
      if (internal.downloadStack.length === 0) {
        return;
      }
 
      // For later use
      const asyncDownloadNextTexture = () => {
        setTimeout(downloadNextTexture, model.waitTimeBetweenDownloads);
      };
 
      const img = new Image();
      if (model.crossOrigin) {
        img.crossOrigin = model.crossOrigin;
      }
 
      const url = internal.downloadStack.shift();
      getRemoteFileSize(url)
        .then((size) => {
          if (!size || size / 1024 > model.maxTextureLODSize) {
            if (!size) {
              console.log('Failed to get image size');
            } else {
              console.log(
                'Skipping image',
                url,
                ', because it is larger',
                'than the max texture size:',
                model.maxTextureLODSize,
                'KiB'
              );
            }
            asyncDownloadNextTexture();
            return;
          }
 
          img.src = url;
          // Decode the image asynchronously in an attempt to prevent a
          // freeze during rendering.
          // In theory, this should help, but my profiling indicates that
          // it does not help much... maybe it is running in the main
          // thread anyways?
          img
            .decode()
            .then(() => {
              model.texture.setImage(img);
              if (model.stepFinishedCallback) {
                model.stepFinishedCallback();
              }
              asyncDownloadNextTexture();
            })
            .catch((encodingError) => {
              console.log('Failed to decode image:', img.src);
              console.log('Error is:', encodingError);
              asyncDownloadNextTexture();
            });
        })
        .catch((xhr) => {
          console.log('Failed to get size of:', url);
          console.log('status was:', xhr.status);
          console.log('statusText was:', xhr.statusText);
          asyncDownloadNextTexture();
        });
    };
 
    setTimeout(downloadNextTexture, model.waitTimeToStart);
  };
}
 
// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------
 
const DEFAULT_VALUES = {
  baseUrl: '',
  files: [],
  texture: null,
  crossOrigin: undefined,
  // The max texture LOD file size in KiB
  maxTextureLODSize: 50000,
  stepFinishedCallback: null,
  // These are in milliseconds
  waitTimeToStart: 4000,
  waitTimeBetweenDownloads: 0,
};
 
// ----------------------------------------------------------------------------
 
export function extend(publicAPI, model, initialValues = {}) {
  Object.assign(model, DEFAULT_VALUES, initialValues);
 
  macro.obj(publicAPI, model);
 
  // Create get-set macros
  macro.setGet(publicAPI, model, [
    'baseUrl',
    'files',
    'texture',
    'crossOrigin',
    'maxTextureLODSize',
    'stepFinishedCallback',
    'waitTimeToStart',
    'waitTimeBetweenDownloads',
  ]);
 
  // Object specific methods
  vtkTextureLODsDownloader(publicAPI, model);
}
 
// ----------------------------------------------------------------------------
 
export const newInstance = macro.newInstance(
  extend,
  'vtkTextureLODsDownloader'
);
 
// ----------------------------------------------------------------------------
 
export default { newInstance, extend };