Skip to content

Commit 9fb3240

Browse files
committed
Merge branch 'master' into neuvue
2 parents b57612e + 9eb296f commit 9fb3240

File tree

11 files changed

+219
-146
lines changed

11 files changed

+219
-146
lines changed

.github/workflows/build.yml

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,67 @@
1-
name: BuildAndDeploy
1+
name: Build
22

3-
on: [push]
3+
on: [push, pull_request]
44

55
jobs:
66
build-and-deploy:
7-
7+
permissions:
8+
contents: 'read'
9+
id-token: 'write'
10+
deployments: 'write'
11+
strategy:
12+
matrix:
13+
node-version:
14+
- '16.x'
815
runs-on: ubuntu-latest
9-
1016
steps:
11-
- uses: actions/checkout@v1
12-
- uses: actions/setup-node@v1
17+
- uses: actions/checkout@v2
18+
- name: Use Node.js ${{ matrix.node-version }}
19+
uses: actions/setup-node@v1
20+
with:
21+
node-version: ${{ matrix.node-version }}
22+
- uses: actions/cache@v3
1323
with:
14-
node-version: '10.x'
15-
- run: npm i
16-
- run: npm run build
24+
path: "**/node_modules"
25+
key: ${{ runner.os }}-${{ matrix.node-version }}-node_modules-${{ hashFiles('**/package-lock.json') }}
26+
- run: npm install
27+
- name: Build
28+
run: npm run build
1729
- run: cp -r ./dist/dev appengine/frontend/static/
18-
- name: Extract branch name
19-
id: get_branch
30+
- name: Get branch name (merge)
31+
if: github.event_name != 'pull_request'
2032
shell: bash
21-
run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/} | tr / - | tr _ -)"
22-
- run: echo ${{ steps.get_branch.outputs.branch }}
33+
run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/} | tr / - | tr _ -)" >> $GITHUB_ENV
34+
- name: Get branch name (pull request)
35+
if: github.event_name == 'pull_request'
36+
shell: bash
37+
run: echo "BRANCH_NAME=$(echo ${GITHUB_HEAD_REF} | tr / - | tr _ -)" >> $GITHUB_ENV
38+
- run: echo ${{ env.BRANCH_NAME }}
2339
- name: start deployment
24-
uses: bobheadxi/deployments@v0.5.2
40+
uses: bobheadxi/deployments@v1
2541
id: deployment
2642
with:
2743
step: start
2844
token: ${{ secrets.GITHUB_TOKEN }}
29-
env: ${{ steps.get_branch.outputs.branch }}
30-
desc: Setting up staging deployment for ${{ steps.get_branch.outputs.branch }}
31-
- name: deploy to gcloud dev branch
32-
uses: actions-hub/gcloud@master
33-
env:
34-
PROJECT_ID: neuromancer-seung-import
35-
APPLICATION_CREDENTIALS: ${{ secrets.SA_NEUROMANCER_GOOGLE_APPS_DEPLOY }}
45+
env: ${{ env.BRANCH_NAME }}
46+
desc: Setting up staging deployment for ${{ env.BRANCH_NAME }}
47+
- id: 'auth'
48+
uses: 'google-github-actions/auth@v1'
49+
with:
50+
workload_identity_provider: 'projects/483670036293/locations/global/workloadIdentityPools/neuroglancer-github/providers/github'
51+
service_account: 'chris-apps-deploy@seung-lab.iam.gserviceaccount.com'
52+
- id: deploy
53+
uses: google-github-actions/deploy-appengine@main
3654
with:
37-
args: app deploy appengine/frontend/app.yaml --no-promote --version ${{ steps.get_branch.outputs.branch }}
55+
version: ${{ env.BRANCH_NAME }}
56+
deliverables: appengine/frontend/app.yaml
57+
promote: false
3858
- name: update deployment status
39-
uses: bobheadxi/deployments@v0.5.2
59+
uses: bobheadxi/deployments@v1
4060
if: always()
4161
with:
4262
step: finish
4363
token: ${{ secrets.GITHUB_TOKEN }}
4464
env: ${{ steps.deployment.outputs.env }}
45-
env_url: https://${{ steps.get_branch.outputs.branch }}-dot-neuromancer-seung-import.appspot.com
65+
env_url: ${{ steps.deploy.outputs.url }}
4666
status: ${{ job.status }}
4767
deployment_id: ${{ steps.deployment.outputs.deployment_id }}

src/neuroglancer/annotation/annotation_layer_view.ts

Lines changed: 71 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ import {StatusMessage} from 'neuroglancer/status';
1313
import {TrackableBoolean, TrackableBooleanCheckbox} from 'neuroglancer/trackable_boolean';
1414
import {getPositionSummary, SelectedAnnotationState, UserLayerWithAnnotations} from 'neuroglancer/ui/annotations';
1515
import {HidingList} from 'neuroglancer/ui/hiding_list';
16+
import {br} from 'neuroglancer/util/br';
1617
import {Borrowed, Owned} from 'neuroglancer/util/disposable';
1718
import {mat4, transformVectorByMat4, vec3} from 'neuroglancer/util/geom';
1819
import {formatIntegerBounds, formatIntegerPoint} from 'neuroglancer/util/spatial_units';
20+
import {Uint64} from 'neuroglancer/util/uint64';
1921
import {ColorWidget} from 'neuroglancer/widget/color';
2022
import {MinimizableGroupWidget} from 'neuroglancer/widget/minimizable_group';
2123
import {RangeWidget} from 'neuroglancer/widget/range';
@@ -314,6 +316,8 @@ export class AnnotationLayerView extends Tab {
314316
const importCSVButton = document.createElement('button');
315317
const importCSVForm = document.createElement('form');
316318
const importCSVFileSelect = document.createElement('input');
319+
const segmentCSVOverrideCheckbox = document.createElement('input');
320+
const segmentCSVOverrideLabel = document.createElement('label');
317321
exportToCSVButton.id = 'exportToCSVButton';
318322
exportToCSVButton.textContent = 'Export to CSV';
319323
exportToCSVButton.addEventListener('click', () => {
@@ -329,12 +333,19 @@ export class AnnotationLayerView extends Tab {
329333
});
330334
importCSVForm.appendChild(importCSVFileSelect);
331335
importCSVFileSelect.addEventListener('change', () => {
332-
this.importCSV(importCSVFileSelect.files);
336+
this.importCSV(importCSVFileSelect.files, segmentCSVOverrideCheckbox.checked);
333337
importCSVForm.reset();
334338
});
335339
importCSVFileSelect.classList.add('neuroglancer-hidden-button');
340+
segmentCSVOverrideLabel.textContent = 'Select segments on import (Experimental): ';
341+
segmentCSVOverrideLabel.title =
342+
'This requires that the segmentation source of the annotation layer when importing the CSV file is the same as the segmentation source when the file was exported. Imported IDs may be outdated.'
343+
segmentCSVOverrideCheckbox.type = 'checkbox';
344+
336345
const csvContainer = document.createElement('span');
337-
csvContainer.append(exportToCSVButton, importCSVButton, importCSVForm);
346+
csvContainer.append(
347+
exportToCSVButton, importCSVButton, br(), segmentCSVOverrideLabel,
348+
segmentCSVOverrideCheckbox, importCSVForm);
338349
this.groupAnnotations.appendFixedChild(csvContainer);
339350
}
340351

@@ -1047,34 +1058,38 @@ export class AnnotationLayerView extends Tab {
10471058
}
10481059

10491060
// TODO: pull request to papa repo
1050-
private betterPapa = (inputFile: File|Blob): Promise<any> => {
1051-
return new Promise((resolve) => {
1052-
Papa.parse(inputFile, {
1053-
complete: (results: any) => {
1054-
resolve(results);
1055-
}
1061+
private betterPapa = (inputFile: File|Blob):
1062+
Promise<any> => {
1063+
return new Promise((resolve) => {
1064+
Papa.parse(inputFile, {
1065+
complete: (results: any) => {
1066+
resolve(results);
1067+
}
1068+
});
10561069
});
1057-
});
1058-
}
1070+
}
10591071

1060-
private stringToVec3 = (input: string): vec3 => {
1061-
// format: (x, y, z)
1062-
let raw = input.split('');
1063-
raw.shift();
1064-
raw.pop();
1065-
let list = raw.join('');
1066-
let val = list.split(',').map(v => parseInt(v, 10));
1067-
return vec3.fromValues(val[0], val[1], val[2]);
1068-
}
1072+
private stringToVec3 = (input: string):
1073+
vec3 => {
1074+
// format: (x, y, z)
1075+
let list = input.replace(/[{()}[\]]/g, '');
1076+
let val = list.split(',').map(v => parseInt(v, 10));
1077+
return vec3.fromValues(val[0], val[1], val[2]);
1078+
}
10691079

1070-
private dimensionsToVec3 = (input: string): vec3 => {
1071-
// format: A × B × C
1072-
let raw = input.replace(/s/g, '');
1073-
let val = raw.split('×').map(v => parseInt(v, 10));
1074-
return vec3.fromValues(val[0], val[1], val[2]);
1075-
}
1080+
private dimensionsToVec3 = (input: string): vec3 => {
1081+
// format: A × B × C
1082+
let raw = input.replace(/s/g, '');
1083+
let val = raw.split('×').map(v => parseInt(v, 10));
1084+
return vec3.fromValues(val[0], val[1], val[2]);
1085+
}
1086+
private textToPoint = (point: string, transform: mat4, dimension?: boolean) => {
1087+
const parsedVec = dimension ? this.dimensionsToVec3(point) : this.stringToVec3(point);
1088+
const spatialPoint = this.voxelSize.spatialFromVoxel(tempVec3, parsedVec);
1089+
return vec3.transformMat4(vec3.create(), spatialPoint, transform);
1090+
}
10761091

1077-
private async importCSV(files: FileList|null) {
1092+
private async importCSV(files: FileList|null, segmentOverride: boolean = false) {
10781093
const rawAnnotations = <Annotation[]>[];
10791094
let successfulImport = 0;
10801095

@@ -1088,61 +1103,59 @@ export class AnnotationLayerView extends Tab {
10881103
if (!rawData.data.length) {
10891104
continue;
10901105
}
1091-
const annStrings = rawData.data;
1106+
const annStrings = <string[][]>rawData.data;
10921107
const csvIdToRealAnnotationIdMap: {[key: string]: string} = {};
10931108
const childStorage: {[key: string]: string[]} = {};
1094-
const textToPoint = (point: string, transform: mat4, dimension?: boolean) => {
1095-
const parsedVec = dimension ? this.dimensionsToVec3(point) : this.stringToVec3(point);
1096-
const spatialPoint = this.voxelSize.spatialFromVoxel(tempVec3, parsedVec);
1097-
return vec3.transformMat4(vec3.create(), spatialPoint, transform);
1098-
};
10991109
let row = -1;
1110+
11001111
for (const annProps of annStrings) {
11011112
row++;
1102-
const type = annProps[7];
1113+
const type = annProps[7].toLowerCase();
11031114
const parentId = annProps[6];
11041115
const annotationID: string|undefined = annProps[8];
11051116
const tags = annProps[3];
1117+
const segments = annProps[5];
11061118
let raw = <Annotation>{id: makeAnnotationId(), description: annProps[4]};
11071119

11081120
switch (type) {
1109-
case 'AABB':
1110-
case 'Line':
1121+
case 'aabb':
1122+
case 'line':
11111123
raw.type =
1112-
type === 'Line' ? AnnotationType.LINE : AnnotationType.AXIS_ALIGNED_BOUNDING_BOX;
1113-
(<Line>raw).pointA = textToPoint(annProps[0], this.annotationLayer.globalToObject);
1114-
(<Line>raw).pointB = textToPoint(annProps[1], this.annotationLayer.globalToObject);
1124+
type === 'line' ? AnnotationType.LINE : AnnotationType.AXIS_ALIGNED_BOUNDING_BOX;
1125+
(<Line>raw).pointA = this.textToPoint(annProps[0], this.annotationLayer.globalToObject);
1126+
(<Line>raw).pointB = this.textToPoint(annProps[1], this.annotationLayer.globalToObject);
11151127
break;
1116-
case 'Point':
1128+
case 'point':
11171129
raw.type = AnnotationType.POINT;
1118-
(<Point>raw).point = textToPoint(annProps[0], this.annotationLayer.globalToObject);
1130+
(<Point>raw).point = this.textToPoint(annProps[0], this.annotationLayer.globalToObject);
11191131
break;
1120-
case 'Ellipsoid':
1132+
case 'ellipsoid':
11211133
raw.type = AnnotationType.ELLIPSOID;
1122-
(<Ellipsoid>raw).center = textToPoint(annProps[0], this.annotationLayer.globalToObject);
1134+
(<Ellipsoid>raw).center =
1135+
this.textToPoint(annProps[0], this.annotationLayer.globalToObject);
11231136
(<Ellipsoid>raw).radii =
1124-
textToPoint(annProps[2], this.annotationLayer.globalToObject, true);
1137+
this.textToPoint(annProps[2], this.annotationLayer.globalToObject, true);
11251138
break;
1126-
case 'Line Strip':
1127-
case 'Line Strip*':
1128-
case 'Spoke':
1129-
case 'Spoke*':
1130-
case 'Collection':
1131-
if (type === 'Line Strip' || type === 'Line Strip*') {
1139+
case 'line Strip':
1140+
case 'line Strip*':
1141+
case 'spoke':
1142+
case 'spoke*':
1143+
case 'collection':
1144+
if (type === 'line Strip' || type === 'line Strip*') {
11321145
raw.type = AnnotationType.LINE_STRIP;
11331146
(<LineStrip>raw).connected = true;
1134-
(<LineStrip>raw).looped = type === 'Line Strip*';
1135-
} else if (type === 'Spoke' || type === 'Spoke*') {
1147+
(<LineStrip>raw).looped = type === 'line Strip*';
1148+
} else if (type === 'spoke' || type === 'spoke*') {
11361149
raw.type = AnnotationType.SPOKE;
11371150
(<Spoke>raw).connected = true;
1138-
(<Spoke>raw).wheeled = type === 'Spoke*';
1151+
(<Spoke>raw).wheeled = type === 'spoke*';
11391152
} else {
11401153
raw.type = AnnotationType.COLLECTION;
11411154
(<Collection>raw).connected = false;
11421155
}
11431156
(<Collection>raw).childrenVisible = new TrackableBoolean(false, true);
11441157
(<Collection>raw).source =
1145-
textToPoint(annProps[0], this.annotationLayer.globalToObject);
1158+
this.textToPoint(annProps[0], this.annotationLayer.globalToObject);
11461159
(<Collection>raw).entry = (index: number) =>
11471160
(<LocalAnnotationSource>this.annotationLayer.source)
11481161
.get((<Collection>raw).entries[index]);
@@ -1192,7 +1205,11 @@ export class AnnotationLayerView extends Tab {
11921205
});
11931206
}
11941207
// Segments not supported
1195-
1208+
// getSelectedAssocatedSegment(this.annotationLayer)
1209+
// naively add segment directly from excel
1210+
if (segments && segmentOverride) {
1211+
raw.segments = segments.split(',').map((s: string) => Uint64.parseString(s));
1212+
}
11961213
rawAnnotations.push(raw);
11971214
}
11981215
successfulImport++;

src/neuroglancer/graph/path_finder_state.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,35 @@ class PathBetweenSupervoxels extends RefCounted {
206206
this.addSourceOrTarget(newTarget);
207207
}
208208

209+
getLatestSegments(layer: SegmentationUserLayerWithGraph) {
210+
if (!this._source || !this._target){
211+
return
212+
}
213+
const {segmentSelectionState} = layer.displayState;
214+
const newSource: Point = {
215+
id: '',
216+
segments: [
217+
segmentSelectionState.rawSelectedSegment.clone(),
218+
segmentSelectionState.selectedSegment.clone()
219+
],
220+
point: this._source.point,
221+
type: AnnotationType.POINT,
222+
};
223+
224+
const newTarget: Point = {
225+
id: '',
226+
segments: [
227+
segmentSelectionState.rawSelectedSegment.clone(),
228+
segmentSelectionState.selectedSegment.clone()
229+
],
230+
point: this._target.point,
231+
type: AnnotationType.POINT,
232+
};
233+
this.clear();
234+
this.addSourceOrTarget(newSource);
235+
this.addSourceOrTarget(newTarget);
236+
}
237+
209238
toJSON() {
210239
const x: any = {
211240
[ANNOTATION_PATH_JSON_KEY]: this.annotationSource.toJSON(),

src/neuroglancer/save_state/save_state.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {Dialog} from 'neuroglancer/dialog';
55
import {Overlay} from 'neuroglancer/overlay';
66
import {dismissUnshareWarning, getSaveToAddressBar, getUnshareWarning} from 'neuroglancer/preferences/user_preferences';
77
import {StatusMessage} from 'neuroglancer/status';
8+
import {br} from 'neuroglancer/util/br';
89
import {RefCounted} from 'neuroglancer/util/disposable';
910
import {getRandomHexString} from 'neuroglancer/util/random';
1011
import {Trackable} from 'neuroglancer/util/trackable';
@@ -200,7 +201,8 @@ export class SaveState extends RefCounted {
200201

201202
this.key = getRandomHexString();
202203
params.set('local_id', this.key);
203-
history.pushState({}, '', `${window.location.origin}${window.location.pathname}?${params.toString()}`);
204+
history.pushState(
205+
{}, '', `${window.location.origin}${window.location.pathname}?${params.toString()}`);
204206
}
205207
private reassign(master: any) {
206208
const hist = <string[]>master.history;
@@ -294,9 +296,8 @@ type FieldConfig = {
294296
};
295297

296298
class SaveDialog extends Overlay {
297-
constructor(public viewer: Viewer, jsonString?: string, getUrlType?: UrlType, hidden = false) {
298-
super(hidden);
299-
const br = () => document.createElement('br');
299+
constructor(public viewer: Viewer, jsonString?: string, getUrlType?: UrlType) {
300+
super();
300301
const jsonURLDefault = `LINK SHORTNER INACCESSIBLE`;
301302

302303
const urlStart = `${window.location.origin}${window.location.pathname}`;

0 commit comments

Comments
 (0)