Skip to content
Snippets Groups Projects
Commit b4e27699 authored by Xiao Gui's avatar Xiao Gui
Browse files

feat: allow zoom in perspective pip

fix: schema generation
chore: gha checkout/setupnode
parent ca209f25
No related branches found
No related tags found
No related merge requests found
......@@ -14,8 +14,8 @@ jobs:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - uses: actions/setup-node@v1
# - uses: actions/checkout@v4
# - uses: actions/setup-node@v4
# with:
# node-version: '16.x'
# - run: npx prettier -c ./src
......@@ -25,9 +25,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js 16.x for lint
uses: actions/setup-node@v1
uses: actions/setup-node@v4
with:
node-version: '16.x'
- run: npm i
......@@ -41,9 +41,9 @@ jobs:
NODE_ENV: test
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js 16.x
uses: actions/setup-node@v1
uses: actions/setup-node@v4
with:
node-version: 16.x
- run: npm i
......@@ -59,7 +59,7 @@ jobs:
if: always()
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: '3.10'
......
......@@ -38,7 +38,7 @@ jobs:
prerelease: false
- name: Use Node.js 16.x
uses: actions/setup-node@v1
uses: actions/setup-node@v4
with:
node-version: '16.x'
......
......@@ -9,3 +9,4 @@
- Fix CI/CD deploy on rancher
- Update API docs
- Update github action checkout/setupnode
......@@ -2,7 +2,7 @@ import ts from 'typescript'
import path, { dirname } from 'path'
import { fileURLToPath } from "url"
import { readFile, writeFile } from "node:fs/promises"
import { clearDirectory, getAllDefs, resolveDef, resolveAllDefs } from "./tsUtil.mjs"
import { clearDirectory, resolveAllDefs } from "./tsUtil.mjs"
import { processNode } from "./tsUtil/index.mjs"
/**
......@@ -53,8 +53,8 @@ async function populateBroadCast(broadcastNode, node){
params: output.properties[prop]
}
}
newSchema = await resolveAllDefs(newSchema, node)
const filename = `${NAMESPACE}.on.${prop}__fromSxplr__request.json`
newSchema = await resolveAllDefs(newSchema, node)
await writeFile(path.join(dirnames.broadcast, filename), JSON.stringify(newSchema, null, 2), 'utf-8')
}
}
......
......@@ -29,9 +29,6 @@
{
"$ref": "#/definitions/AtId"
},
{
"$ref": "#/schemas/CoordinatePointModel/"
},
{
"type": "object",
"properties": {
......@@ -40,10 +37,127 @@
},
"color": {
"type": "string"
},
"openminds": {
"$ref": "#/components/schemas/CoordinatePointModel"
}
}
}
]
},
"AtId": {
"type": "object",
"properties": {
"@id": {
"type": "string"
}
}
}
},
"components": {
"schemas": {
"CoordinatePointModel": {
"properties": {
"@type": {
"type": "string",
"title": "@Type"
},
"@context": {
"allOf": [
{
"$ref": "#/components/schemas/VocabModel"
}
],
"title": "@Context",
"default": {
"@vocab": "https://openminds.ebrains.eu/vocab/"
}
},
"@id": {
"type": "string",
"title": "@Id",
"description": "Metadata node identifier."
},
"coordinateSpace": {
"type": "object",
"title": "coordinateSpace",
"description": "Two or three dimensional geometric setting."
},
"coordinates": {
"items": {
"$ref": "#/components/schemas/api__models__openminds__SANDS__v3__miscellaneous__coordinatePoint__Coordinates"
},
"type": "array",
"title": "Coordinates",
"description": "Structured information on a quantitative value."
}
},
"type": "object",
"required": [
"@type",
"@id",
"coordinateSpace",
"coordinates"
],
"title": "CoordinatePointModel",
"description": "CoordinatePointModel"
},
"VocabModel": {
"properties": {
"@vocab": {
"type": "string",
"title": "@Vocab"
}
},
"type": "object",
"required": [
"@vocab"
],
"title": "VocabModel"
},
"api__models__openminds__SANDS__v3__miscellaneous__coordinatePoint__Coordinates": {
"properties": {
"@context": {
"allOf": [
{
"$ref": "#/components/schemas/VocabModel"
}
],
"title": "@Context",
"default": {
"@vocab": "https://openminds.ebrains.eu/vocab/"
}
},
"typeOfUncertainty": {
"title": "typeOfUncertainty",
"description": "Distinct technique used to quantify the uncertainty of a measurement."
},
"uncertainty": {
"items": {
"type": "number"
},
"type": "array",
"maxItems": 2,
"minItems": 2,
"title": "uncertainty",
"description": "Quantitative value range defining the uncertainty of a measurement."
},
"unit": {
"title": "unit",
"description": "Determinate quantity adopted as a standard of measurement."
},
"value": {
"type": "number",
"title": "value",
"description": "Entry for a property."
}
},
"type": "object",
"required": [
"value"
],
"title": "Coordinates"
}
}
}
}
\ No newline at end of file
......@@ -96,6 +96,13 @@ function setDictValue(d, key, value){
}
}
/**
*
* @description Get the value (if exist), otherwise return null. Does not mutate
* @param {Object} d
* @param {string} key
* @returns {JSchema|null}
*/
function getDictValue(d, key) {
const keys = key.replace(/^#\//, '').split("/")
let returnValue = d
......@@ -148,8 +155,6 @@ const defMap = {
SxplrCoordinatePointExtension: {
allOf: [{
$ref: "#/definitions/AtId"
}, {
$ref: "#/schemas/CoordinatePointModel/"
}, {
type: "object",
properties: {
......@@ -158,15 +163,26 @@ const defMap = {
},
color: {
type: "string"
},
openminds: {
$ref: "#/components/schemas/CoordinatePointModel"
}
}
}]
},
AtId: {
type: "object",
properties: {
"@id": {
type: "string"
}
}
}
}
},
"#/definitions/AtId": {
definitions: {
"AtId": {
AtId: {
type: "object",
properties: {
"@id": {
......@@ -264,11 +280,21 @@ let openApi = null
* @returns {Promise<Object<string, Object<string, JSchema>>>}
*/
export async function resolveDef(def, src){
/**
* @type {JSchema}
*/
let schema = null
const fullDef = def
const trimmedDef = def.replace("#/definitions/", "")
const found = defMap[def]
if(found) {
return found
schema = found
if (trimmedDef === "AtId" || trimmedDef === "AddableLayer") {
return schema
}
}
if (!openApi) {
const openapiText = await readFile(path.resolve(__dirname, "../atlasComponents/sapi/openapi.json"), "utf-8")
......@@ -294,10 +320,6 @@ export async function resolveDef(def, src){
return JSON.parse(JSON.stringify(traversal))
}
/**
* @type {JSchema}
*/
let schema = null
src.forEachChild(n => {
if (ts.SyntaxKind[n.kind] === "TypeAliasDeclaration") {
if (n.name.text === trimmedDef) {
......@@ -334,6 +356,8 @@ export async function resolveDef(def, src){
}
let allDefs = getAllDefs(schema)
// ensure the defs are not yet defined
allDefs = allDefs.filter(def => !getDictValue(schema, def))
let cb = 0
while (true) {
......@@ -373,7 +397,6 @@ export async function resolveAllDefs(schema, node){
*/
let newSchema = schema
const allDefs = getAllDefs(newSchema)
for (const def of allDefs) {
const resolvedDefs = await resolveDef(def, node)
newSchema = {
......
......@@ -581,3 +581,12 @@ export function getShaderFromMeta(meta: MetaV1Schema){
highThreshold: high
})
}
export function isNullish(v: unknown){
return v === null || typeof v === "undefined"
}
export function isWheelEvent(e: unknown): e is WheelEvent{
const { deltaX, deltaY } = (e || {}) as any
return !isNullish(deltaX) && !isNullish(deltaY)
}
import { Component, Inject, ViewChild, ChangeDetectionStrategy, inject, HostListener } from "@angular/core";
import { FormControl } from "@angular/forms";
import { select, Store } from "@ngrx/store";
import { BehaviorSubject, combineLatest, concat, NEVER, Observable, of, Subject } from "rxjs";
import { BehaviorSubject, combineLatest, concat, merge, NEVER, Observable, of, Subject } from "rxjs";
import { switchMap, distinctUntilChanged, map, debounceTime, shareReplay, take, withLatestFrom, filter, takeUntil } from "rxjs/operators";
import { SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes"
import { selectedTemplate } from "src/state/atlasSelection/selectors";
......@@ -15,6 +15,7 @@ import { atlasSelection } from "src/state";
import { floatEquality } from "common/util"
import { CURRENT_TEMPLATE_DIM_INFO, TemplateInfo } from "../../layerCtrl.service/layerCtrl.util";
import { DestroyDirective } from "src/util/directives/destroy.directive";
import { isNullish, isWheelEvent } from "src/util/fn"
const MAX_DIM = 200
......@@ -43,6 +44,11 @@ function getDim(triplet: number[], view: EnumClassicalView) {
}
}
type ModArr = {
idx: number
value: number
}
@Component({
selector: 'nehuba-perspective-view-slider',
templateUrl: './perspectiveViewSlider.template.html',
......@@ -87,6 +93,14 @@ export class PerspectiveViewSlider {
this.#mouseup.next(true)
}
#zoom = new Subject<number>()
mousewheel(ev: Event){
if (!isWheelEvent(ev)) {
return
}
this.#zoom.next(ev.deltaY)
}
#destroy$ = inject(DestroyDirective).destroyed$
@ViewChild(ResizeObserverDirective)
......@@ -382,43 +396,82 @@ export class PerspectiveViewSlider {
@Inject(CURRENT_TEMPLATE_DIM_INFO) private tmplInfo$: Observable<TemplateInfo>,
) {
combineLatest([
this.nehubaViewer$,
this.rangeControlSetting$,
]).pipe(
switchMap(([ nehubaViewer, rangeCtrl ]) => combineLatest([
this.minimapControl.valueChanges,
concat(
of(true),
this.#dragging
),
]).pipe(
map(([ newValue, _ ]) => newValue),
const posMod$ = this.rangeControlSetting$.pipe(
switchMap(rangeCtrl => this.#dragging.pipe(
withLatestFrom(
this.navPosition$.pipe(
map(value => value?.real)
),
this.minimapControl.valueChanges,
this.currentTemplateSize$,
this.#xyRatio,
),
map(([newValue, currentPosition, currTmplSize, xyRatio]) => ({ nehubaViewer, rangeCtrl, newValue, currentPosition, currTmplSize, xyRatio }))
)),
map(([_, newValue, currTmplSize, xyRatio]) => {
const positionMod: ModArr[] = []
const { anatomicalOrientation } = rangeCtrl
if (!isNullish(anatomicalOrientation) && !isNullish(newValue)) {
const idx = anatOriToIdx[anatomicalOrientation]
positionMod.push({
idx,
value: newValue
})
}
if (!isNullish(xyRatio.x) && !isNullish(xyRatio.y)) {
const { idx, value } = anaOriAltAxis[anatomicalOrientation](currTmplSize.real, xyRatio)
positionMod.push({
idx,
value
})
}
return { positionMod, zoom: null as number }
})
))
)
const zoom$ = this.nehubaViewer$.pipe(
switchMap(nehubaViewer => this.#zoom.pipe(
withLatestFrom(nehubaViewer
? nehubaViewer.viewerPositionChange
: NEVER),
map(([zoom, posChange]) => {
const { zoom: currZoom } = posChange
return {
zoom: zoom > 0 ? currZoom * 1.2 : currZoom * 0.8,
positionMod: null as ModArr[]
}
})
))
)
this.nehubaViewer$.pipe(
switchMap(nehubaViewer =>
merge(
posMod$,
zoom$,
).pipe(
map(({ positionMod, zoom }) => ({
nehubaViewer, positionMod, zoom
}))
)
),
withLatestFrom(
this.navPosition$.pipe(
map(value => value?.real)
),
),
takeUntil(this.#destroy$)
).subscribe(({ nehubaViewer, rangeCtrl, newValue, currentPosition, currTmplSize, xyRatio }) => {
if (newValue === null) return
const { anatomicalOrientation } = rangeCtrl
if (!anatomicalOrientation) return
const idx = anatOriToIdx[anatomicalOrientation]
const newNavPosition = [...currentPosition]
newNavPosition[idx] = newValue
).subscribe(([{ nehubaViewer, positionMod, zoom }, currentPosition]) => {
if (!(xyRatio.x === xyRatio.y === null)) {
const { idx: altIdx, value } = anaOriAltAxis[anatomicalOrientation](currTmplSize.real, xyRatio)
newNavPosition[altIdx] = value
const newNavPosition = [...currentPosition]
if (!isNullish(positionMod)) {
for (const { idx, value } of positionMod) {
newNavPosition[idx] = value
}
}
nehubaViewer.setNavigationState({
position: newNavPosition,
...(isNullish(zoom) ? {} : { zoom }),
positionReal: true
})
})
......
<div class="range-container"
(mousemove)="mousemove($event)"
(mousedown)="mousedown()"
(mousewheel)="mousewheel($event)"
*ngIf="sliceviewIsNormal$ | async else resetOrientationTmpl">
<img *ngIf="previewImageUrl$ | async as url" [src]="url">
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment