vtkPiecewiseFunction Defines a piecewise function mapping. This mapping allows the addition of control points, and allows the user to control the function between the control points. A piecewise hermite curve is used between control points, based on the sharpness and midpoint parameters. A sharpness of 0 yields a piecewise linear function and a sharpness of 1 yields a piecewise constant function. The midpoint is the normalized distance between control points at which the curve reaches the median Y value.
The midpoint and sharpness values specified when adding a node are used to control the transition to the next node (the last node’s values are ignored) Outside the range of nodes, the values are 0 if Clamping is off, or the nearest node point if Clamping is on. Using the legacy methods for adding points (which do not have Sharpness and Midpoint parameters) will default to Midpoint = 0.5 (halfway between the control points) and Sharpness = 0.0 (linear).
Remove all points out of the new range, and make sure there is a point at each end of that range.
Argument
Type
Required
Description
range
Range
Yes
estimateMinNumberOfSamples
Estimates the minimum size of a table such that it would correctly sample this function.
extend
Method used to decorate a given object (publicAPI+model) with vtkPiecewiseFunction characteristics.
Argument
Type
Required
Description
publicAPI
Yes
object on which methods will be bounds (public)
model
Yes
object on which data structure will be bounds (protected)
initialValues
IPiecewiseFunctionInitialValues
No
(default: {})
findMinimumXDistance
Traverses the nodes to find the minimum distance.
getAllowDuplicateScalars
Toggle whether to allow duplicate scalar values in the piecewise function (off by default).
getClamping
When zero range clamping is Off, GetValue() returns 0.0 when a value is requested outside of the points specified.
When zero range clamping is On, GetValue() returns the value at the value at the lowest point for a request below all points specified and returns the value at the highest point for a request above all points specified. On is the default.
getDataPointer
Returns a pointer to the data stored in the table.
getFirstNonZeroValue
Returns the first point location which precedes a non-zero segment of the function.
getNodeValue
For the node specified by index, set/get the location (X), value (Y), midpoint, and sharpness values at the node.
Argument
Type
Required
Description
index
Number
Yes
val
Yes
getRange
Returns the min and max node locations of the function.
getRangeByReference
Returns the min and max node locations of the function.
getSize
Get the number of points used to specify the function.
getTable
Fills in an array of function values evaluated at regular intervals.
Argument
Type
Required
Description
xStart
Number
Yes
xEnd
Number
Yes
size
Number
Yes
table
Yes
stride
Number
No
getType
Return the type of function: Function Types:
0 : Constant (No change in slope between end points)
1 : NonDecreasing (Always increasing or zero slope)
2 : NonIncreasing (Always decreasing or zero slope)
3 : Varied (Contains both decreasing and increasing slopes)
getValue
Returns the value of the function at the specified location using the specified interpolation.
newInstance
Method used to create a new instance of vtkPiecewiseFunction.
Argument
Type
Required
Description
initialValues
IPiecewiseFunctionInitialValues
No
for pre-setting some of its content
removeAllPoints
Removes all points from the function.
removePoint
Remove the first point found at the given x location Return the index of the remove point if any, -1 otherwise.
Argument
Type
Required
Description
x
Number
Yes
setAllowDuplicateScalars
Argument
Type
Required
Description
allowDuplicateScalars
Boolean
Yes
setClamping
When zero range clamping is Off, GetValue() returns 0.0 when a value is requested outside of the points specified.
When zero range clamping is On, GetValue() returns the value at the value at the lowest point for a request below all points specified and returns the value at the highest point for a request above all points specified. On is the default.
Argument
Type
Required
Description
clamping
Boolean
Yes
setNodeValue
Argument
Type
Required
Description
index
Number
Yes
val
Yes
setNodes
Argument
Type
Required
Description
nodes
Yes
setRange
Argument
Type
Required
Description
min
Number
Yes
max
Number
Yes
setRange
Argument
Type
Required
Description
range
Range
Yes
setRangeFrom
Argument
Type
Required
Description
range
Range
Yes
sortAndUpdateRange
Internal method to sort the vector and update the Range whenever a node is added, edited or removed.
updateRange
Returns true if the range has been updated and Modified() has been called.
Source
index.d.ts
import { vtkObject } from'../../../interfaces'; import { Range } from'../../../types';
export interface vtkPiecewiseFunction extends vtkObject { /** * Add points to the function. * @param {Number} x The x coordinate. * @param {Number} y The y coordinate. */ addPoint(x: number, y: number): void;
/** * Add points to the function. * @param {Number} x The x coordinate. * @param {Number} y The y coordinate. * @param {Number} midpoint * @param {Number} sharpness */ addPointLong( x: number, y: number, midpoint: number, sharpness: number ): number;
/** * Add a line segment to the function. * @param {Number} x1 The first point x coordinate. * @param {Number} y1 The first point y coordinate. * @param {Number} x2 The second point x coordinate. * @param {Number} y2 The second point y coordinate. */ addSegment(x1: number, y1: number, x2: number, y2: number): void;
/** * Remove all points out of the new range, and make sure there is a point at * each end of that range. * @param {Range} range */ adjustRange(range: Range): number;
/** * Estimates the minimum size of a table such that it would correctly sample * this function. */ estimateMinNumberOfSamples(): number;
/** * Traverses the nodes to find the minimum distance. */ findMinimumXDistance(): number;
/** * Toggle whether to allow duplicate scalar values in the piecewise function * (off by default). */ getAllowDuplicateScalars(): boolean;
/** * When zero range clamping is Off, GetValue() returns 0.0 when a value is * requested outside of the points specified. * * When zero range clamping is On, GetValue() returns the value at the value * at the lowest point for a request below all points specified and returns * the value at the highest point for a request above all points specified. * On is the default. */ getClamping(): boolean;
/** * Returns a pointer to the data stored in the table. */ getDataPointer(): any[];
/** * Returns the first point location which precedes a non-zero segment of the * function. */ getFirstNonZeroValue(): number;
/** * For the node specified by index, set/get the location (X), value (Y), * midpoint, and sharpness values at the node. * @param {Number} index * @paramval */ getNodeValue(index: number, val: any[]): void;
/** * Returns the min and max node locations of the function. */ getRange(): Range;
/** * Returns the min and max node locations of the function. */ getRangeByReference(): Range;
/** * Get the number of points used to specify the function. */ getSize(): number;
/** * Fills in an array of function values evaluated at regular intervals. * @param {Number} xStart * @param {Number} xEnd * @param {Number} size * @paramtable * @param {Number} [stride] */ getTable( xStart: number, xEnd: number, size: number, table: any, stride?: number ): void;
/** * Return the type of function: Function Types: * * 0 : Constant (No change in slope between end points) * * 1 : NonDecreasing (Always increasing or zero slope) * * 2 : NonIncreasing (Always decreasing or zero slope) * * 3 : Varied (Contains both decreasing and increasing slopes) */ getType(): 'Constant' | 'NonDecreasing' | 'NonIncreasing' | 'Varied';
/** * Returns the value of the function at the specified location using the * specified interpolation. */ getValue(): any;
/** * Removes all points from the function. */ removeAllPoints(): void;
/** * Remove the first point found at the given x location Return the index of * the remove point if any, -1 otherwise. * @param {Number} x */ removePoint(x: number): number;
/** * When zero range clamping is Off, GetValue() returns 0.0 when a value is * requested outside of the points specified. * * When zero range clamping is On, GetValue() returns the value at the value * at the lowest point for a request below all points specified and returns * the value at the highest point for a request above all points specified. * On is the default. * @param {Boolean} clamping */ setClamping(clamping: boolean): boolean;
/** * * @param {Range} range */ setRange(range: Range): boolean;
/** * * @param {Number} min * @param {Number} max */ setRange(min: number, max: number): boolean;
/** * * @param {Range} range */ setRangeFrom(range: Range): boolean;
/** * Internal method to sort the vector and update the Range whenever a node * is added, edited or removed. */ sortAndUpdateRange(): void;
/** * Returns true if the range has been updated and Modified() has been * called. */ updateRange(): boolean; }
/** * Method used to decorate a given object (publicAPI+model) with vtkPiecewiseFunction characteristics. * * @param publicAPI object on which methods will be bounds (public) * @param model object on which data structure will be bounds (protected) * @param {IPiecewiseFunctionInitialValues} [initialValues] (default: {}) */ exportfunctionextend( publicAPI: object, model: object, initialValues?: IPiecewiseFunctionInitialValues ): void;
/** * Method used to create a new instance of vtkPiecewiseFunction. * @param {IPiecewiseFunctionInitialValues} [initialValues] for pre-setting some of its content */ exportfunctionnewInstance( initialValues?: IPiecewiseFunctionInitialValues ): vtkPiecewiseFunction;
/** * vtkPiecewiseFunction Defines a piecewise function mapping. This mapping * allows the addition of control points, and allows the user to control the * function between the control points. A piecewise hermite curve is used * between control points, based on the sharpness and midpoint parameters. A * sharpness of 0 yields a piecewise linear function and a sharpness of 1 yields * a piecewise constant function. The midpoint is the normalized distance * between control points at which the curve reaches the median Y value. * * The midpoint and sharpness values specified when adding a node are used to * control the transition to the next node (the last node's values are ignored) * Outside the range of nodes, the values are 0 if Clamping is off, or the * nearest node point if Clamping is on. Using the legacy methods for adding * points (which do not have Sharpness and Midpoint parameters) will default to * Midpoint = 0.5 (halfway between the control points) and Sharpness = 0.0 * (linear). * * @example * ```js * const ofun = vtkPiecewiseFunction.newInstance(); * ofun.addPoint(200.0, 0.0); * ofun.addPoint(1200.0, 0.2); * ofun.addPoint(4000.0, 0.4); * ``` * * @see [vtkColorTransferFunction](./Rendering_Core_ColorTransferFunction.html) */ export declare constvtkPiecewiseFunction: { newInstance: typeof newInstance; extend: typeof extend; }; exportdefault vtkPiecewiseFunction;
functionvtkPiecewiseFunction(publicAPI, model) { // Set our className model.classHierarchy.push('vtkPiecewiseFunction');
// Return the number of points which specify this function publicAPI.getSize = () => model.nodes.length;
// Return the type of function stored in object: // Function Types: // 0 : Constant (No change in slope between end points) // 1 : NonDecreasing (Always increasing or zero slope) // 2 : NonIncreasing (Always decreasing or zero slope) // 3 : Varied (Contains both decreasing and increasing slopes) // 4 : Unknown (Error condition) // publicAPI.getType = () => { let value; let prevValue = 0.0; let functionType = 0;
if (model.nodes.length > 0) { prevValue = model.nodes[0].y; }
for (let i = 1; i < model.nodes.length; i++) { value = model.nodes[i].y;
// Do not change the function type if equal if (value !== prevValue) { if (value > prevValue) { switch (functionType) { case0: case1: // NonDecreasing functionType = 1; break; case2: default: // Varied functionType = 3; break; } } else { // value < prev_value switch (functionType) { case0: case2: // NonIncreasing functionType = 2; break; case1: default: // Varied functionType = 3; break; } } }
prevValue = value;
// Exit loop if we find a Varied function if (functionType === 3) { break; } }
// Since we no longer store the data in an array, we must // copy out of the vector into an array. No modified check - // could be added if performance is a problem publicAPI.getDataPointer = () => { const size = model.nodes.length;
model.function = null;
if (size > 0) { model.function = []; for (let i = 0; i < size; i++) { model.function[2 * i] = model.nodes[i].x; model.function[2 * i + 1] = model.nodes[i].y; } } return model.function; };
// Returns the first point location which starts a non-zero segment of the // function. Note that the value at this point may be zero. publicAPI.getFirstNonZeroValue = () => { // Check if no points specified if (model.nodes.length === 0) { return0; }
let allZero = 1; let x = 0.0; let i = 0; for (; i < model.nodes.length; i++) { if (model.nodes[i].y !== 0.0) { allZero = 0; break; } }
// If every specified point has a zero value then return // a large value if (allZero) { x = Number.MAX_VALUE; } elseif (i > 0) { // A point was found with a non-zero value // Return the value of the point that precedes this one x = model.nodes[i - 1].x; } elseif (model.clamping) { // If this is the first point in the function, return its // value is clamping is off, otherwise VTK_DOUBLE_MIN if // clamping is on. x = -Number.MAX_VALUE; } else { x = model.nodes[0].x; }
return x; };
// For a specified index value, get the node parameters publicAPI.getNodeValue = (index, val) => { const size = model.nodes.length;
if (index < 0 || index >= size) { vtkErrorMacro('Index out of range!'); return -1; }
if (oldX !== val[0]) { // The point has been moved, the order of points or the range might have // been modified. publicAPI.sortAndUpdateRange(); // No need to call Modified() here because SortAndUpdateRange() has done it // already. } else { publicAPI.modified(); }
return1; };
// Adds a point to the function. If a duplicate point is inserted // then the function value at that location is set to the new value. // This is the legacy version that assumes midpoint = 0.5 and // sharpness = 0.0 publicAPI.addPoint = (x, y) => publicAPI.addPointLong(x, y, 0.5, 0.0);
// Adds a point to the function and returns the array index of the point. publicAPI.addPointLong = (x, y, midpoint, sharpness) => { // Error check if (midpoint < 0.0 || midpoint > 1.0) { vtkErrorMacro('Midpoint outside range [0.0, 1.0]'); return -1; }
if (sharpness < 0.0 || sharpness > 1.0) { vtkErrorMacro('Sharpness outside range [0.0, 1.0]'); return -1; }
// remove any node already at this X location if (!model.allowDuplicateScalars) { publicAPI.removePoint(x); }
// Create the new node const node = { x, y, midpoint, sharpness };
// Add it, then sort to get everything in order model.nodes.push(node); publicAPI.sortAndUpdateRange();
// Now find this node so we can return the index let i; for (i = 0; i < model.nodes.length; i++) { if (model.nodes[i].x === x) { break; } }
// If we didn't find it, something went horribly wrong so // return -1 if (i < model.nodes.length) { return i; }
// Sort the vector in increasing order, then fill in // the Range publicAPI.sortAndUpdateRange = () => { model.nodes.sort((a, b) => a.x - b.x); const modifiedInvoked = publicAPI.updateRange(); // If range is updated, Modified() has been called, don't call it again. if (!modifiedInvoked) { publicAPI.modified(); } };
const size = model.nodes.length; if (size) { model.range[0] = model.nodes[0].x; model.range[1] = model.nodes[size - 1].x; } else { model.range[0] = 0; model.range[1] = 0; } // If the rage is the same, then no need to call Modified() if (oldRange[0] === model.range[0] && oldRange[1] === model.range[1]) { returnfalse; }
publicAPI.modified(); returntrue; };
// Removes a point from the function. If no point is found then function // remains the same. publicAPI.removePoint = (x) => { // First find the node since we need to know its // index as our return value let i; for (i = 0; i < model.nodes.length; i++) { if (model.nodes[i].x === x) { break; } }
// If the node doesn't exist, we return -1 if (i >= model.nodes.length) { return -1; }
const retVal = i;
// If the first or last point has been removed, then we update the range // No need to sort here as the order of points hasn't changed. let modifiedInvoked = false; model.nodes.splice(i, 1); if (i === 0 || i === model.nodes.length) { modifiedInvoked = publicAPI.updateRange(); } if (!modifiedInvoked) { publicAPI.modified(); }
return retVal; };
// Removes all points from the function. publicAPI.removeAllPoints = () => { model.nodes = []; publicAPI.sortAndUpdateRange(); };
// Add in end points of line and remove any points between them // Legacy method with no way to specify midpoint and sharpness publicAPI.addSegment = (x1, y1, x2, y2) => { // First, find all points in this range and remove them publicAPI.sortAndUpdateRange(); for (let i = 0; i < model.nodes.length; ) { if (model.nodes[i].x >= x1 && model.nodes[i].x <= x2) { model.nodes.splice(i, 1); } else { i++; } }
// Now add the points publicAPI.addPoint(x1, y1, 0.5, 0.0); publicAPI.addPoint(x2, y2, 0.5, 0.0); };
// Return the value of the function at a position publicAPI.getValue = (x) => { const table = []; publicAPI.getTable(x, x, 1, table); return table[0]; };
// Remove all points outside the range, and make sure a point // exists at each end of the range. Used as a convenience method // for transfer function editors publicAPI.adjustRange = (range) => { if (range.length < 2) { return0; }
const functionRange = publicAPI.getRange();
// Make sure we have points at each end of the range if (functionRange[0] < range[0]) { publicAPI.addPoint(range[0], publicAPI.getValue(range[0])); } else { publicAPI.addPoint(range[0], publicAPI.getValue(functionRange[0])); }
let distance = model.nodes[1].x - model.nodes[0].x; for (let i = 0; i < size - 1; i++) { const currentDist = model.nodes[i + 1].x - model.nodes[i].x; if (currentDist < distance) { distance = currentDist; } }
return distance; };
// Returns a table of function values evaluated at regular intervals /* eslint-disable prefer-destructuring */ /* eslint-disable no-continue */ publicAPI.getTable = (xStart, xEnd, size, table, stride = 1) => { let i; let idx = 0; const numNodes = model.nodes.length;
// Need to keep track of the last value so that // we can fill in table locations past this with // this value if Clamping is On. let lastValue = 0.0; if (numNodes !== 0) { lastValue = model.nodes[numNodes - 1].y; }
let x = 0.0; let x1 = 0.0; let x2 = 0.0; let y1 = 0.0; let y2 = 0.0; let midpoint = 0.0; let sharpness = 0.0;
// For each table entry for (i = 0; i < size; i++) { // Find our location in the table const tidx = stride * i;
// Find our X location. If we are taking only 1 sample, make // it halfway between start and end (usually start and end will // be the same in this case) if (size > 1) { x = xStart + (i / (size - 1.0)) * (xEnd - xStart); } else { x = 0.5 * (xStart + xEnd); }
// Do we need to move to the next node? while (idx < numNodes && x > model.nodes[idx].x) { idx++; // If we are at a valid point index, fill in // the value at this node, and the one before (the // two that surround our current sample location) // idx cannot be 0 since we just incremented it. if (idx < numNodes) { x1 = model.nodes[idx - 1].x; x2 = model.nodes[idx].x;
// We only need the previous midpoint and sharpness // since these control this region midpoint = model.nodes[idx - 1].midpoint; sharpness = model.nodes[idx - 1].sharpness;
// Move midpoint away from extreme ends of range to avoid // degenerate math if (midpoint < 0.00001) { midpoint = 0.00001; }
// Are we at the end? If so, just use the last value if (idx >= numNodes) { table[tidx] = model.clamping ? lastValue : 0.0; } elseif (idx === 0) { // Are we before the first node? If so, duplicate this nodes values table[tidx] = model.clamping ? model.nodes[0].y : 0.0; } else { // Otherwise, we are between two nodes - interpolate // Our first attempt at a normalized location [0,1] - // we will be modifying this based on midpoint and // sharpness to get the curve shape we want and to have // it pass through (y1+y2)/2 at the midpoint. let s = (x - x1) / (x2 - x1);
// Readjust based on the midpoint - linear adjustment if (s < midpoint) { s = (0.5 * s) / midpoint; } else { s = 0.5 + (0.5 * (s - midpoint)) / (1.0 - midpoint); }
// override for sharpness > 0.99 // In this case we just want piecewise constant if (sharpness > 0.99) { // Use the first value since we are below the midpoint if (s < 0.5) { table[tidx] = y1; continue; } else { // Use the second value at or above the midpoint table[tidx] = y2; continue; } }
// Override for sharpness < 0.01 // In this case we want piecewise linear if (sharpness < 0.01) { // Simple linear interpolation table[tidx] = (1 - s) * y1 + s * y2; continue; }
// We have a sharpness between [0.01, 0.99] - we will // used a modified hermite curve interpolation where we // derive the slope based on the sharpness, and we compress // the curve non-linearly based on the sharpness
// First, we will adjust our position based on sharpness in // order to make the curve sharper (closer to piecewise constant) if (s < 0.5) { s = 0.5 * (s * 2) ** (1.0 + 10 * sharpness); } elseif (s > 0.5) { s = 1.0 - 0.5 * ((1.0 - s) * 2) ** (1 + 10 * sharpness); }
// Compute some coefficients we will need for the hermite curve const ss = s * s; const sss = ss * s;