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 | 430x 175x 175x 175x 430x 1345x 1345x 4218x 366x 366x 175x 366x 3852x 1496x 2356x 1210x 4523x 1146x 1345x 9312x 9312x 7583x 1729x 4590x 199x | import type { Types } from '@cornerstonejs/core';
import { utilities as csUtils } from '@cornerstonejs/core';
const { PointsManager } = csUtils;
type OmitKeyHandler = ((key: string, value: unknown) => unknown) | null;
function cloneContourValue(_key: string, value: unknown): unknown {
Iif (value == null || typeof value !== 'object' || !('polyline' in value)) {
return value;
}
const contour = value as { polyline: unknown[]; [k: string]: unknown };
return {
...contour,
polyline: null,
pointsManager: PointsManager.create3(
contour.polyline.length,
contour.polyline as Types.Point3[]
),
};
}
/**
* Keys that are known to hold large or non-cloneable data (e.g. in annotation
* data or cachedStats). When the value is null, the key is omitted. When the
* value is a function, it is called with (key, value) and the return value is
* used as the new value for that key.
* - pointsInVolume: large array of Point3 (e.g. CircleROIStartEndThresholdTool)
* - projectionPoints: array of arrays of Point3 (e.g. CircleROIStartEndThresholdTool)
* - contour: cloneable via polyline → pointsManager (handled by cloneContourValue)
* - spline: annotation-specific object with non-cloneable refs (omitted)
*/
const OMIT_KEYS = new Map<string, OmitKeyHandler>([
['pointsInVolume', null],
['projectionPoints', null],
['contour', cloneContourValue],
['spline', null],
]);
/**
* Recursively copies an object, omitting known large/non-cloneable keys.
*/
function omitUncloneableKeys(
obj: Record<string, unknown>
): Record<string, unknown> {
const result: Record<string, unknown> = {};
for (const [key, value] of Object.entries(obj)) {
if (OMIT_KEYS.has(key)) {
const handler = OMIT_KEYS.get(key);
if (handler) {
result[key] = handler(key, value);
}
continue;
}
if (value === null || value === undefined || typeof value !== 'object') {
result[key] = value;
} else if (Array.isArray(value)) {
result[key] = value.map((value) =>
safeStructuredClone(value as Record<string, unknown>)
);
} else {
result[key] = omitUncloneableKeys(value as Record<string, unknown>);
}
}
return result;
}
/**
* Like structuredClone, but safe for annotation-style data: omits known large
* or non-cloneable keys (e.g. pointsInVolume, projectionPoints) and replaces
* any value that cannot be cloned with null if the whole clone fails.
* Use for cloning annotation data or other objects that may contain large
* arrays or non-cloneable references.
*
* @param value - Any value (typically annotation data object).
* @returns Deep copy with omit-keys stripped and uncloneable values nulled on fallback.
*/
export function safeStructuredClone<T>(value: T): T {
Iif (value === null || value === undefined) {
return value;
}
if (typeof value !== 'object') {
return value;
}
if (Array.isArray(value)) {
return value.map((item) => safeStructuredClone(item)) as unknown as T;
}
return omitUncloneableKeys(value as Record<string, unknown>) as T;
}
|