// namespaces
var dwv = dwv || {};
dwv.image = dwv.image || {};
/**
* Get an simple iterator for a given range for a one component data.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
* @param {Function} dataAccessor Function to access data.
* @param {number} start The start of the range (included).
* @param {number} end The end of the range (excluded).
* @param {number} increment The increment between indicies (default=1).
* @returns {object} An iterator folowing the iterator and iterable protocol.
*/
dwv.image.simpleRange = function (dataAccessor, start, end, increment) {
if (typeof increment === 'undefined') {
increment = 1;
}
var nextIndex = start;
// result
return {
next: function () {
if (nextIndex < end) {
var result = {
value: dataAccessor(nextIndex),
done: false
};
nextIndex += increment;
return result;
}
return {
done: true,
index: end
};
}
};
};
/**
* Get an iterator for a given range for a one component data.
*
* Using 'maxIter' and not an 'end' index since it fails in some edge cases
* (for ex coronal2, ie zxy)
*
* @param {Function} dataAccessor Function to access data.
* @param {number} start Zero-based index at which to start the iteration.
* @param {number} maxIter The maximum number of iterations.
* @param {number} increment Increment between indicies.
* @param {number} blockMaxIter Number of applied increment after which
* blockIncrement is applied.
* @param {number} blockIncrement Increment after blockMaxIter is reached,
* the value is from block start to the next block start.
* @param {boolean} reverse1 If true, loop from end to start.
* WARN: don't forget to set the value of start as the last index!
* @param {boolean} reverse2 If true, loop from block end to block start.
* @returns {object} An iterator folowing the iterator and iterable protocol.
*/
dwv.image.range = function (dataAccessor, start, maxIter, increment,
blockMaxIter, blockIncrement, reverse1, reverse2) {
if (typeof reverse1 === 'undefined') {
reverse1 = false;
}
if (typeof reverse2 === 'undefined') {
reverse2 = false;
}
// first index of the iteration
var nextIndex = start;
// adapt first index and increments to reverse values
if (reverse1) {
blockIncrement *= -1;
if (reverse2) {
// start at end of line
nextIndex -= (blockMaxIter - 1) * increment;
} else {
increment *= -1;
}
} else {
if (reverse2) {
// start at end of line
nextIndex += (blockMaxIter - 1) * increment;
increment *= -1;
}
}
var finalBlockIncrement = blockIncrement - blockMaxIter * increment;
// counters
var mainCount = 0;
var blockCount = 0;
// result
return {
next: function () {
if (mainCount < maxIter) {
var result = {
value: dataAccessor(nextIndex),
done: false
};
nextIndex += increment;
++mainCount;
++blockCount;
if (blockCount === blockMaxIter) {
blockCount = 0;
nextIndex += finalBlockIncrement;
}
return result;
}
return {
done: true,
index: nextIndex
};
}
};
};
/**
* Get an iterator for a given range with bounds (for a one component data).
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
* @param {Function} dataAccessor Function to access data.
* @param {number} start The start of the range (included).
* @param {number} end The end of the range (excluded).
* @param {number} increment The increment between indicies.
* @param {number} regionSize The size of the region to iterate through.
* @param {number} regionOffset The offset between regions.
* @returns {object} An iterator folowing the iterator and iterable protocol.
*/
dwv.image.rangeRegion = function (
dataAccessor, start, end, increment, regionSize, regionOffset) {
var nextIndex = start;
var regionElementCount = 0;
// result
return {
next: function () {
if (nextIndex < end) {
var result = {
value: dataAccessor(nextIndex),
done: false
};
regionElementCount += 1;
nextIndex += increment;
if (regionElementCount === regionSize) {
regionElementCount = 0;
nextIndex += regionOffset;
}
return result;
}
return {
done: true,
index: end
};
}
};
};
/**
* Get an iterator for a given range with bounds (for a one component data).
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
* @param {Function} dataAccessor Function to access data.
* @param {number} start The start of the range (included).
* @param {number} end The end of the range (excluded).
* @param {number} increment The increment between indicies.
* @param {Array} regions An array of regions: [off0, size, off1].
* @returns {object} An iterator folowing the iterator and iterable protocol.
*/
dwv.image.rangeRegions = function (
dataAccessor, start, end, increment, regions) {
var nextIndex = start;
var regionCount = 0;
var regionElementCount = 0;
// result
return {
next: function () {
if (nextIndex < end) {
var result = {
value: dataAccessor(nextIndex),
done: false
};
regionElementCount += 1;
nextIndex += increment;
if (regionElementCount === regions[regionCount][1]) {
regionElementCount = 0;
// off1 of current group
nextIndex += regions[regionCount][2];
regionCount += 1;
// off0 of next group
if (regionCount < regions.length) {
nextIndex += regions[regionCount][0];
}
}
return result;
}
return {
done: true,
index: end
};
}
};
};
/**
* Get an iterator for a given range for a 3 components data.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
* @param {Function} dataAccessor Function to access data.
* @param {number} start The start of the range (included).
* @param {number} end The end of the range (excluded).
* (end - start) needs to be a multiple of 3...
* @param {number} increment The increment between indicies (default=1).
* @param {boolean} isPlanar A flag to know if the data is planar
* (RRRR...GGGG...BBBB...) or not (RGBRGBRGBRGB...), defaults to false.
* @returns {object} A 3 components iterator folowing the iterator and iterable
* protocol, with extra 'value1' and 'value2' for the second and
* third component.
*/
dwv.image.range3d = function (dataAccessor, start, end, increment, isPlanar) {
if (typeof increment === 'undefined') {
increment = 1;
}
if (typeof isPlanar === 'undefined') {
isPlanar = false;
}
var nextIndex = start;
var componentIncrement = 1;
if (isPlanar) {
componentIncrement = (end - start) / 3;
} else {
increment *= 3;
}
var nextIndex1 = nextIndex + componentIncrement;
var nextIndex2 = nextIndex + 2 * componentIncrement;
// result
return {
next: function () {
if (nextIndex < end) {
var result = {
value: [
dataAccessor(nextIndex),
dataAccessor(nextIndex1),
dataAccessor(nextIndex2)
],
done: false
};
nextIndex += increment;
nextIndex1 += increment;
nextIndex2 += increment;
return result;
}
return {
done: true,
index: [end]
};
}
};
};
/**
* Get a list of values for a given iterator.
*
* @param {object} iterator The iterator to use to loop through data.
* @returns {Array} The list of values.
*/
dwv.image.getIteratorValues = function (iterator) {
var values = [];
var ival = iterator.next();
while (!ival.done) {
values.push(ival.value);
ival = iterator.next();
}
return values;
};
/**
* Get a slice index iterator.
*
* @param {dwv.image.Image} image The image to parse.
* @param {dwv.math.Point} position The current position.
* @param {boolean} isRescaled Flag for rescaled values (default false).
* @param {dwv.math.Matrix33} viewOrientation The view orientation.
* @returns {object} The slice iterator.
*/
dwv.image.getSliceIterator = function (
image, position, isRescaled, viewOrientation) {
var size = image.getGeometry().getSize();
// zero-ify non direction index
var dirMax2Index = 2;
if (viewOrientation && typeof viewOrientation !== 'undefined') {
dirMax2Index = viewOrientation.getColAbsMax(2).index;
}
var posValues = position.getValues();
// keep the main direction and any other than 3D
var indexFilter = function (element, index) {
return (index === dirMax2Index || index > 2) ? element : 0;
};
var posStart = new dwv.math.Index(posValues.map(indexFilter));
var start = size.indexToOffset(posStart);
// default to non rescaled data
if (typeof isRescaled === 'undefined') {
isRescaled = false;
}
var dataAccessor = null;
if (isRescaled) {
dataAccessor = function (offset) {
return image.getRescaledValueAtOffset(offset);
};
} else {
dataAccessor = function (offset) {
return image.getValueAtOffset(offset);
};
}
var ncols = size.get(0);
var nrows = size.get(1);
var nslices = size.get(2);
var sliceSize = size.getDimSize(2);
var range = null;
if (image.getNumberOfComponents() === 1) {
if (viewOrientation && typeof viewOrientation !== 'undefined') {
var dirMax0 = viewOrientation.getColAbsMax(0);
var dirMax2 = viewOrientation.getColAbsMax(2);
// default reverse
var reverse1 = false;
var reverse2 = false;
var maxIter = null;
if (dirMax2.index === 2) {
// axial
maxIter = ncols * nrows;
if (dirMax0.index === 0) {
// xyz
range = dwv.image.range(dataAccessor,
start, maxIter, 1, ncols, ncols, reverse1, reverse2);
} else {
// yxz
range = dwv.image.range(dataAccessor,
start, maxIter, ncols, nrows, 1, reverse1, reverse2);
}
} else if (dirMax2.index === 0) {
// sagittal
maxIter = nslices * nrows;
if (dirMax0.index === 1) {
// yzx
range = dwv.image.range(dataAccessor,
start, maxIter, ncols, nrows, sliceSize, reverse1, reverse2);
} else {
// zyx
range = dwv.image.range(dataAccessor,
start, maxIter, sliceSize, nslices, ncols, reverse1, reverse2);
}
} else if (dirMax2.index === 1) {
// coronal
maxIter = nslices * ncols;
if (dirMax0.index === 0) {
// xzy
range = dwv.image.range(dataAccessor,
start, maxIter, 1, ncols, sliceSize, reverse1, reverse2);
} else {
// zxy
range = dwv.image.range(dataAccessor,
start, maxIter, sliceSize, nslices, 1, reverse1, reverse2);
}
} else {
throw new Error('Unknown direction: ' + dirMax2.index);
}
} else {
// default case
range = dwv.image.simpleRange(dataAccessor, start, start + sliceSize);
}
} else if (image.getNumberOfComponents() === 3) {
// 3 times bigger...
start *= 3;
sliceSize *= 3;
var isPlanar = image.getPlanarConfiguration() === 1;
range = dwv.image.range3d(
dataAccessor, start, start + sliceSize, 1, isPlanar);
} else {
throw new Error('Unsupported number of components: ' +
image.getNumberOfComponents());
}
return range;
};
/**
* Get a slice index iterator for a rectangular region.
*
* @param {dwv.image.Image} image The image to parse.
* @param {dwv.math.Point} position The current position.
* @param {boolean} isRescaled Flag for rescaled values (default false).
* @param {dwv.math.Point2D} min The minimum position (optional).
* @param {dwv.math.Point2D} max The maximum position (optional).
* @returns {object} The slice iterator.
*/
dwv.image.getRegionSliceIterator = function (
image, position, isRescaled, min, max) {
if (image.getNumberOfComponents() !== 1) {
throw new Error('Unsupported number of components for region iterator: ' +
image.getNumberOfComponents());
}
// default to non rescaled data
if (typeof isRescaled === 'undefined') {
isRescaled = false;
}
var dataAccessor = null;
if (isRescaled) {
dataAccessor = function (offset) {
return image.getRescaledValueAtOffset(offset);
};
} else {
dataAccessor = function (offset) {
return image.getValueAtOffset(offset);
};
}
var size = image.getGeometry().getSize();
if (typeof min === 'undefined') {
min = new dwv.math.Point2D(0, 0);
}
if (typeof max === 'undefined') {
max = new dwv.math.Point2D(
size.get(0) - 1,
size.get(1)
);
}
// position to pixel for max: extra X is ok, remove extra Y
var startOffset = size.indexToOffset(position.getWithNew2D(
min.getX(), min.getY()
));
var endOffset = size.indexToOffset(position.getWithNew2D(
max.getX(), max.getY() - 1
));
// minimum 1 column
var rangeNumberOfColumns = Math.max(1, max.getX() - min.getX());
var rowIncrement = size.get(0) - rangeNumberOfColumns;
return dwv.image.rangeRegion(
dataAccessor, startOffset, endOffset + 1,
1, rangeNumberOfColumns, rowIncrement);
};
/**
* Get a slice index iterator for a rectangular region.
*
* @param {dwv.image.Image} image The image to parse.
* @param {dwv.math.Point} position The current position.
* @param {boolean} isRescaled Flag for rescaled values (default false).
* @param {Array} regions An array of regions.
* @returns {object} The slice iterator.
*/
dwv.image.getVariableRegionSliceIterator = function (
image, position, isRescaled, regions) {
if (image.getNumberOfComponents() !== 1) {
throw new Error('Unsupported number of components for region iterator: ' +
image.getNumberOfComponents());
}
// default to non rescaled data
if (typeof isRescaled === 'undefined') {
isRescaled = false;
}
var dataAccessor = null;
if (isRescaled) {
dataAccessor = function (offset) {
return image.getRescaledValueAtOffset(offset);
};
} else {
dataAccessor = function (offset) {
return image.getValueAtOffset(offset);
};
}
var size = image.getGeometry().getSize();
var offsetRegions = [];
var region;
var min = null;
var max = null;
var index = null;
for (var i = 0; i < regions.length; ++i) {
region = regions[i];
var width = region[1][0] - region[0][0];
if (width !== 0) {
index = i;
if (!min) {
min = region[0];
}
offsetRegions.push([
region[0][0],
width,
size.get(0) - region[1][0]
]);
}
}
if (index !== null) {
max = regions[index][1];
}
// exit if no offsets
if (offsetRegions.length === 0) {
return;
}
var startOffset = size.indexToOffset(position.getWithNew2D(
min[0], min[1]
));
var endOffset = size.indexToOffset(position.getWithNew2D(
max[0], max[1]
));
return dwv.image.rangeRegions(
dataAccessor, startOffset, endOffset + 1,
1, offsetRegions);
};