diff --git a/src/atlasComponents/sapi/core/space/interspaceLinearXform.ts b/src/atlasComponents/sapi/core/space/interspaceLinearXform.ts new file mode 100644 index 0000000000000000000000000000000000000000..44c93c74186567e3967d2224cb4937ee391de215 --- /dev/null +++ b/src/atlasComponents/sapi/core/space/interspaceLinearXform.ts @@ -0,0 +1,60 @@ +export const VALID_LINEAR_XFORM_SRC = { + CCF: "Allen Common Coordination Framework" +} + +export const VALID_LINEAR_XFORM_DST = { + NEHUBA: "nehuba" +} + +export type TVALID_LINEAR_XFORM_SRC = keyof typeof VALID_LINEAR_XFORM_SRC +export type TVALID_LINEAR_XFORM_DST = keyof typeof VALID_LINEAR_XFORM_DST + +type TLinearXform = number[][] + +const _linearXformDict: Record< + keyof typeof VALID_LINEAR_XFORM_SRC, + Record< + keyof typeof VALID_LINEAR_XFORM_DST, + TLinearXform + >> = { + CCF: { + NEHUBA: [ + [-1e3, 0, 0, 11400000 - 5737500], // + [0, 0, -1e3, 13200000 - 6637500], // + [0, -1e3, 0, 8000000 - 4037500], // + [0, 0, 0, 1], + ] + } + } + +const defaultXform = [ + [1e3, 0, 0, 0], + [0, 1e3, 0, 0], + [0, 0, 1e3, 0], + [0, 0, 0, 1], +] + + +const getProxyXform = <T>(obj: Record<string, T>, cb: (value: T) => T) => new Proxy({}, { + get: (_target, prop: string, _receiver) => { + return cb(obj[prop]) + } +}) + +export const linearXformDict = getProxyXform(_linearXformDict, (value: Record<string, TLinearXform>) => { + if (!value) return getProxyXform({}, () => defaultXform) + return getProxyXform(value, (v: TLinearXform) => { + if (v) return v + return defaultXform + }) +}) as Record< + keyof typeof VALID_LINEAR_XFORM_SRC, + Record< + keyof typeof VALID_LINEAR_XFORM_DST, + TLinearXform + >> + + +export const linearTransform = async (srcTmplName: keyof typeof VALID_LINEAR_XFORM_SRC, targetTmplName: keyof typeof VALID_LINEAR_XFORM_DST) => { + return linearXformDict[srcTmplName][targetTmplName] +} diff --git a/src/atlasComponents/userAnnotations/filterAnnotationBySpace.pipe.ts b/src/atlasComponents/userAnnotations/filterAnnotationBySpace.pipe.ts index fb6500281577a0791f6c82b7d53ce6b7aef636a6..d339398c0a55865d2a4fce6e2feb9246e4a5606b 100644 --- a/src/atlasComponents/userAnnotations/filterAnnotationBySpace.pipe.ts +++ b/src/atlasComponents/userAnnotations/filterAnnotationBySpace.pipe.ts @@ -14,7 +14,7 @@ export class FilterAnnotationsBySpace implements PipeTransform{ public transform(annotations: IAnnotationGeometry[], space: { '@id': string }, opts?: TOpts): IAnnotationGeometry[]{ const { reverse = false } = opts || {} return reverse - ? annotations.filter(ann => ann.space["@id"] !== space["@id"]) - : annotations.filter(ann => ann.space["@id"] === space["@id"]) + ? annotations.filter(ann => ann.space?.["@id"] !== space?.["@id"]) + : annotations.filter(ann => ann.space?.["@id"] === space?.["@id"]) } -} \ No newline at end of file +} diff --git a/src/viewerModule/nehuba/config.service/util.ts b/src/viewerModule/nehuba/config.service/util.ts index 7233d0f26bb2bda42bfe3b0141c9c9b64a377c77..8f2b0de2dc144f287815a503dd95e35c741e82fb 100644 --- a/src/viewerModule/nehuba/config.service/util.ts +++ b/src/viewerModule/nehuba/config.service/util.ts @@ -409,6 +409,11 @@ export function getNehubaConfig(space: SapiSpaceModel): NehubaConfig { "layers": {}, "navigation": { "zoomFactor": 350000 * scale, + pose: { + position: { + voxelSize: [1, 1, 1] + } + } }, "perspectiveOrientation": [ 0.3140767216682434, diff --git a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts index 136c0d0a314394628ddccdcd91010e85420f7524..da9855ecb93b0344c2239776590f0625dd20117a 100644 --- a/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts +++ b/src/viewerModule/nehuba/layerCtrl.service/layerCtrl.service.ts @@ -176,14 +176,23 @@ export class NehubaLayerControlService implements OnDestroy{ * on custom landmarks loaded, set mesh transparency */ this.sub.push( - this.store$.pipe( - select(annotation.selectors.annotations), + merge( + this.store$.pipe( + select(annotation.selectors.annotations), + map(landmarks => landmarks.length > 0), + ), + this.store$.pipe( + select(atlasAppearance.selectors.customLayers), + map(customLayers => customLayers.filter(l => l.clType === "customlayer/nglayer" && /^swc:\/\//.test(l.source)).length > 0), + ) + ).pipe( + startWith(false), withLatestFrom(this.defaultNgLayers$) - ).subscribe(([landmarks, { tmplAuxNgLayers }]) => { + ).subscribe(([flag, { tmplAuxNgLayers }]) => { const payload: { [key: string]: number } = {} - const alpha = landmarks.length > 0 + const alpha = flag ? 0.2 : 1.0 for (const ngId in tmplAuxNgLayers) { diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts index fea1911266af6a410bdd339c805c4aeb3129f20c..5c16b0a065f1f5b2d14be81d724230ed30b7b301 100644 --- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts +++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts @@ -20,6 +20,7 @@ import { SapiRegionModel } from "src/atlasComponents/sapi"; import { NehubaConfig, getParcNgId, getRegionLabelIndex } from "../config.service"; import { SET_MESHES_TO_LOAD } from "../constants"; import { annotation, atlasAppearance, atlasSelection, userInteraction } from "src/state"; +import { linearTransform, TVALID_LINEAR_XFORM_DST, TVALID_LINEAR_XFORM_SRC } from "src/atlasComponents/sapi/core/space/interspaceLinearXform"; export const INVALID_FILE_INPUT = `Exactly one (1) nifti file is required!` @@ -287,6 +288,20 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnDestroy, AfterViewIni this.dismissAllAddedLayers() if (/\.swc$/i.test(file.name)) { + let message = `The swc rendering is experimental. Please contact us on any feedbacks. ` + const swcText = await file.text() + let src: TVALID_LINEAR_XFORM_SRC + const dst: TVALID_LINEAR_XFORM_DST = "NEHUBA" + if (/ccf/i.test(swcText)) { + src = "CCF" + message += `CCF detected, applying known transformation.` + } + if (!src) { + message += `no known space detected. Applying default transformation.` + } + + const xform = await linearTransform(src, dst) + const url = URL.createObjectURL(file) this.droppedLayerNames.push({ layerName: randomUuid, @@ -298,16 +313,14 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnDestroy, AfterViewIni id: randomUuid, source: `swc://${url}`, segments: ["1"], - transform: [ - [1e3, 0, 0, 0], - [0, 1e3, 0, 0], - [0, 0, 1e3, 0], - [0, 0, 0, 1], - ], + transform: xform, clType: 'customlayer/nglayer' as const } }) ) + this.snackbar.open(message, "Dismiss", { + duration: 10000 + }) return }