Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 126 additions & 32 deletions build-pack-vitessce-zarronly.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$ROOT_DIR"

VERSION="${1:-3.8.8-mh-zarronly.0}"
PACK_DIR="${2:-lib_vitessce}"
PACK_DIR="${2:-lib_vitessce_zarronly_${VERSION}}"

PACKAGES=(
"packages/constants-internal"
Expand All @@ -26,6 +26,25 @@ PACKAGES=(
"packages/file-types/spatial-zarr"
)

PACKAGE_NAMES=(
"@vitessce/constants-internal"
"@vitessce/error"
"@vitessce/plugins"
"@vitessce/types"
"@vitessce/globals"
"@vitessce/abstract"
"@vitessce/constants"
"@vitessce/utils"
"@vitessce/zarr-utils"
"@vitessce/config"
"@vitessce/schemas"
"@vitessce/spatial-utils"
"@vitessce/image-utils"
"@vitessce/sets-utils"
"@vitessce/zarr"
"@vitessce/spatial-zarr"
)

MANIFESTS=(
"packages/file-types/abstract/package.json"
"packages/config/package.json"
Expand All @@ -46,25 +65,82 @@ MANIFESTS=(
"packages/constants-internal/src/version.json"
)

echo "Setting Vitessce zarr-only package version to ${VERSION}"
VERSION="$VERSION" node - "${MANIFESTS[@]}" <<'NODE'
BACKUP_DIR="$(mktemp -d)"

restore_manifests() {
local file

for file in "${MANIFESTS[@]}"; do
if [[ -f "$BACKUP_DIR/$file" ]]; then
cp "$BACKUP_DIR/$file" "$file"
fi
done

if [[ -f "$BACKUP_DIR/pnpm-lock.yaml" ]]; then
cp "$BACKUP_DIR/pnpm-lock.yaml" pnpm-lock.yaml
fi

rm -rf "$BACKUP_DIR"
}

trap restore_manifests EXIT

backup_file() {
local file="$1"

mkdir -p "$BACKUP_DIR/$(dirname "$file")"
cp "$file" "$BACKUP_DIR/$file"
}

for file in "${MANIFESTS[@]}"; do
backup_file "$file"
done

if [[ -f pnpm-lock.yaml ]]; then
cp pnpm-lock.yaml "$BACKUP_DIR/pnpm-lock.yaml"
fi

update_manifests() {
local mode="$1"

VERSION="$VERSION" MODE="$mode" node - "${MANIFESTS[@]}" <<'NODE'
const fs = require('fs');

const version = process.env.VERSION;
const mode = process.env.MODE;
const files = process.argv.slice(2);

for (const file of files) {
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
data.version = version;

if (file.endsWith('/package.json')) {
const dependencies = data.dependencies || {};
const peerDependencies = data.peerDependencies || {};
const dependencies = { ...(data.dependencies || {}) };
const peerDependencies = { ...(data.peerDependencies || {}) };
const devDependencies = { ...(data.devDependencies || {}) };
const internalNames = new Set(
[...Object.keys(dependencies), ...Object.keys(peerDependencies)]
.filter((name) => name.startsWith('@vitessce/')),
);

for (const name of internalNames) {
delete dependencies[name];
peerDependencies[name] = version;

if (mode === 'build') {
devDependencies[name] = 'workspace:*';
} else if (mode === 'pack') {
delete devDependencies[name];
} else {
throw new Error(`Unknown manifest update mode: ${mode}`);
}
}

for (const name of Object.keys(dependencies)) {
if (name.startsWith('@vitessce/')) {
peerDependencies[name] = version;
delete dependencies[name];
if (mode === 'pack') {
for (const name of Object.keys(devDependencies)) {
if (name.startsWith('@vitessce/')) {
delete devDependencies[name];
}
}
}

Expand All @@ -79,37 +155,55 @@ for (const file of files) {
} else {
delete data.peerDependencies;
}

if (Object.keys(devDependencies).length > 0) {
data.devDependencies = devDependencies;
} else {
delete data.devDependencies;
}
}

fs.writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`);
}
NODE
}

echo "Building bundled output for zarr-only package closure"
pnpm \
--filter @vitessce/abstract \
--filter @vitessce/config \
--filter @vitessce/constants \
--filter @vitessce/constants-internal \
--filter @vitessce/error \
--filter @vitessce/globals \
--filter @vitessce/image-utils \
--filter @vitessce/plugins \
--filter @vitessce/schemas \
--filter @vitessce/sets-utils \
--filter @vitessce/spatial-utils \
--filter @vitessce/spatial-zarr \
--filter @vitessce/types \
--filter @vitessce/utils \
--filter @vitessce/zarr \
--filter @vitessce/zarr-utils \
--workspace-concurrency 1 \
bundle

echo "Building TypeScript declaration output"
echo "Checking Node.js version"
node <<'NODE'
const [major, minor] = process.versions.node.split('.').map(Number);
const ok = major > 22 || (major === 22 && minor >= 12) || (major === 20 && minor >= 19);

if (!ok) {
console.error(`Node.js ${process.versions.node} detected. Vite requires Node.js 20.19+ or 22.12+.`);
process.exit(1);
}
NODE

echo "Preparing temporary build manifests for ${VERSION}"
update_manifests build

echo "Installing dependencies"
pnpm install --force --no-frozen-lockfile

echo "Cleaning previous build output"
pnpm run clean

echo "Building TypeScript output"
pnpm exec tsc --build

FILTER_ARGS=()
for package_name in "${PACKAGE_NAMES[@]}"; do
FILTER_ARGS+=(--filter "$package_name")
done

echo "Building bundled output for zarr-only package closure"
pnpm -r "${FILTER_ARGS[@]}" --workspace-concurrency 1 bundle

echo "Preparing pack manifests for ${VERSION}"
update_manifests pack

mkdir -p "$PACK_DIR"
find "$PACK_DIR" -maxdepth 1 -type f -name "vitessce-*-${VERSION}.tgz" -delete

echo "Packing tarballs into ${PACK_DIR}"
for package_dir in "${PACKAGES[@]}"; do
Expand All @@ -119,4 +213,4 @@ done
echo "Packed files:"
find "$PACK_DIR" -maxdepth 1 -type f -name "vitessce-*-${VERSION}.tgz" -print | sort

echo "Done."
echo "Done. Original manifests and pnpm-lock.yaml have been restored."
22 changes: 16 additions & 6 deletions packages/file-types/spatial-zarr/src/ome-loaders/OmeZarrLoader.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
initializeRasterLayersAndChannels,
coordinateTransformationsToMatrix,
coordinateTransformationsToMatrixForSpatialData,
getNgffAxes,
hexToRgb,
normalizeCoordinateTransformations,
Expand Down Expand Up @@ -60,16 +61,25 @@ export default class OmeZarrLoader extends AbstractTwoStepLoader {
// Reference: https://github.com/ome/ngff/pull/138

// This new spec is very flexible, so here we will attepmpt to convert it back to the old spec.
const normCoordinateTransformationsFromFile = normalizeCoordinateTransformations(
coordinateTransformationsFromFile, datasets,
);
const { coordinateSystem } = this.options || {};
let transformMatrixFromFile;
if (coordinateSystem && Array.isArray(coordinateTransformationsFromFile)) {
transformMatrixFromFile = coordinateTransformationsToMatrixForSpatialData(
{ datasets, coordinateTransformations: coordinateTransformationsFromFile, axes },
coordinateSystem,
);
} else {
const normCoordinateTransformationsFromFile = normalizeCoordinateTransformations(
coordinateTransformationsFromFile, datasets,
);
transformMatrixFromFile = coordinateTransformationsToMatrix(
normCoordinateTransformationsFromFile, axes,
);
}

const transformMatrixFromOptions = coordinateTransformationsToMatrix(
coordinateTransformationsFromOptions, axes,
);
const transformMatrixFromFile = coordinateTransformationsToMatrix(
normCoordinateTransformationsFromFile, axes,
);

const transformMatrix = transformMatrixFromFile.multiplyLeft(transformMatrixFromOptions);

Expand Down
118 changes: 82 additions & 36 deletions packages/utils/spatial-utils/src/spatial.js
Original file line number Diff line number Diff line change
Expand Up @@ -499,24 +499,52 @@ export function coordinateTransformationsToMatrix(coordinateTransformations, axe
mat = mat.multiplyLeft(swapMat);
}
} else if (spatialOutputAxes.length === 2) { // 2D case
const nextMat = (new Matrix4()).fromArray([
filteredAffine[0][0], filteredAffine[0][1], 0, filteredAffine[0][2],
filteredAffine[1][0], filteredAffine[1][1], 0, filteredAffine[1][2],
0, 0, 1, 0,
0, 0, 0, 1,
]);
mat = mat.multiplyLeft(nextMat);

if (!isEqual(inputAxisNames, outputAxisNames)) {
// Handle 2D axis swapping.
const swapMatNested = getSwapAxesMatrix(inputAxisNames, outputAxisNames);
const swapMat = (new Matrix4()).fromArray(swapMatNested.flat());
mat = mat.multiplyLeft(swapMat);
// Build the column-major Matrix4 directly from the OME-NGFF affine,
// mapping output axes to world x/y and input axes to world x/y.
// The previous implementation filled a row-major 4x4 matrix, applied
// an axis swap on the wrong side, and then transposed the whole
// accumulated matrix, which turned valid affines into projective
// transforms whenever rotation or shear was present.
const inputAxisIndex = {};
inputAxisNames.forEach((name, i) => { inputAxisIndex[name] = i; });
const outputAxisIndex = {};
outputAxisNames.forEach((name, i) => { outputAxisIndex[name] = i; });

const inIdxX = inputAxisIndex.x;
const inIdxY = inputAxisIndex.y;
const outIdxX = outputAxisIndex.x;
const outIdxY = outputAxisIndex.y;
if (
inIdxX === undefined
|| inIdxY === undefined
|| outIdxX === undefined
|| outIdxY === undefined
) {
throw new Error('2D affine transformation is missing x or y axis mapping.');
}
// TODO: is the transpose needed? why?
// TODO: is transpose only needed when axis-swapping?
// TODO: is it also needed in the 3D case? Why was it not needed before?
mat = mat.transpose();

const affineX = filteredAffine[outIdxX];
const affineY = filteredAffine[outIdxY];
const colMajor = [
affineX[inIdxX] ?? 0,
affineY[inIdxX] ?? 0,
0,
0,
affineX[inIdxY] ?? 0,
affineY[inIdxY] ?? 0,
0,
0,
0,
0,
1,
0,
affineX[2] ?? 0,
affineY[2] ?? 0,
0,
1,
];
const nextMat = (new Matrix4()).fromArray(colMajor);
mat = mat.multiplyLeft(nextMat);
} else {
throw new Error('Affine transformation must have 2 or 3 rows.');
}
Expand Down Expand Up @@ -556,6 +584,18 @@ export function coordinateTransformationsToMatrix(coordinateTransformations, axe

// TODO: error if the user tries to use a scale on the "c" axis.
}
if (transform.type === 'mapAxis') {
const { input, output } = transform;
const spatialInputAxes = input.axes.filter(axis => axis.type === 'space');
const spatialOutputAxes = output.axes.filter(axis => axis.type === 'space');
const transformInputAxisNames = spatialInputAxes.map(axis => axis.name);
const transformOutputAxisNames = spatialOutputAxes.map(axis => axis.name);
if (!isEqual(transformInputAxisNames, transformOutputAxisNames)) {
const swapMatNested = getSwapAxesMatrix(transformInputAxisNames, transformOutputAxisNames);
const swapMat = (new Matrix4()).fromArray(swapMatNested.flat());
mat = mat.multiplyLeft(swapMat);
}
}
});
}
if (mat.some(value => Number.isNaN(value))) {
Expand Down Expand Up @@ -612,26 +652,32 @@ export function normalizeCoordinateTransformations(coordinateTransformations, da
let result = [];

if (Array.isArray(coordinateTransformations)) {
result = coordinateTransformations.flatMap((transform) => {
if (transform.input && transform.output) {
// This is a new-style coordinate transformation.
// (As proposed in https://github.com/ome/ngff/pull/138)
const { type } = transform;
if (type === 'sequence') {
// Recursion to flatten the sequence of transformations.
return normalizeCoordinateTransformations(transform.transformations, null);
} if (type === 'affine' || type === 'translation' || type === 'scale' || type === 'identity') {
// TODO: normalize the transform.input/transform.output if they are missing?
// Old transformations did not specify them for translation/scale/identity.
// But we are currently only using them for affine.
return transform;
const hasSequence = coordinateTransformations.some((transform) => transform.type === 'sequence');
if (hasSequence) {
const sequenceTransforms = coordinateTransformations.filter((transform) => transform.type === 'sequence');
result = sequenceTransforms.flatMap((transform) => normalizeCoordinateTransformations(transform.transformations, null));
} else {
result = coordinateTransformations.flatMap((transform) => {
if (transform.input && transform.output) {
// This is a new-style coordinate transformation.
// (As proposed in https://github.com/ome/ngff/pull/138)
const { type } = transform;
if (type === 'sequence') {
// Recursion to flatten the sequence of transformations.
return normalizeCoordinateTransformations(transform.transformations, null);
} if (type === 'affine' || type === 'translation' || type === 'scale' || type === 'identity') {
// TODO: normalize the transform.input/transform.output if they are missing?
// Old transformations did not specify them for translation/scale/identity.
// But we are currently only using them for affine.
return transform;
}
// If the type is not recognized, log an error.
log.error(`Coordinate transformation type "${type}" is not supported.`);
}
// If the type is not recognized, log an error.
log.error(`Coordinate transformation type "${type}" is not supported.`);
}
// Assume it was already an old-style (NGFF v0.4) coordinate transformation.
return transform;
});
// Assume it was already an old-style (NGFF v0.4) coordinate transformation.
return transform;
});
}
}

if (Array.isArray(datasets?.[0]?.coordinateTransformations)) {
Expand Down
Loading