Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Annotation features mostly related to MultiscaleAnnotationSource #493

Closed
wants to merge 3 commits into from
Closed
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
9 changes: 1 addition & 8 deletions src/neuroglancer/annotation/annotation_layer_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,7 @@ export class WatchableAnnotationRelationshipStates extends
nestedContext.registerDisposer(segmentationGroupState.changed.add(this.changed.dispatch));
nestedContext.registerDisposer(registerNested((groupContext, groupState) => {
const {visibleSegments} = groupState;
let wasEmpty = visibleSegments.size === 0;
groupContext.registerDisposer(visibleSegments.changed.add(() => {
const isEmpty = visibleSegments.size === 0;
if (isEmpty !== wasEmpty) {
wasEmpty = isEmpty;
this.changed.dispatch();
}
}));
groupContext.registerDisposer(visibleSegments.changed.add(this.changed.dispatch));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was simplified because I need the annotation list to update anytime the segment list changes

}, segmentationGroupState));
}, segmentationState));
});
Expand Down
73 changes: 72 additions & 1 deletion src/neuroglancer/annotation/frontend_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {Annotation, AnnotationId, AnnotationPropertySerializer, AnnotationProper
import {ANNOTATION_COMMIT_UPDATE_RESULT_RPC_ID, ANNOTATION_COMMIT_UPDATE_RPC_ID, ANNOTATION_GEOMETRY_CHUNK_SOURCE_RPC_ID, ANNOTATION_METADATA_CHUNK_SOURCE_RPC_ID, ANNOTATION_REFERENCE_ADD_RPC_ID, ANNOTATION_REFERENCE_DELETE_RPC_ID, ANNOTATION_SUBSET_GEOMETRY_CHUNK_SOURCE_RPC_ID, AnnotationGeometryChunkSpecification} from 'neuroglancer/annotation/base';
import {getAnnotationTypeRenderHandler} from 'neuroglancer/annotation/type_handler';
import {Chunk, ChunkManager, ChunkSource} from 'neuroglancer/chunk_manager/frontend';
import {getObjectKey} from 'neuroglancer/segmentation_display_state/base';
import {forEachVisibleSegment, getObjectKey} from 'neuroglancer/segmentation_display_state/base';
import {SliceViewSourceOptions} from 'neuroglancer/sliceview/base';
import {MultiscaleSliceViewChunkSource, SliceViewChunk, SliceViewChunkSource, SliceViewChunkSourceOptions, SliceViewSingleResolutionSource} from 'neuroglancer/sliceview/frontend';
import {StatusMessage} from 'neuroglancer/status';
Expand All @@ -29,6 +29,8 @@ import {NullarySignal, Signal} from 'neuroglancer/util/signal';
import {Buffer} from 'neuroglancer/webgl/buffer';
import {GL} from 'neuroglancer/webgl/context';
import {registerRPC, registerSharedObjectOwner, RPC, SharedObject} from 'neuroglancer/worker_rpc';
import {AnnotationLayerState} from 'neuroglancer/annotation/annotation_layer_state';
import {ChunkState} from 'neuroglancer/chunk_manager/base';

export interface AnnotationGeometryChunkSourceOptions extends SliceViewChunkSourceOptions {
spec: AnnotationGeometryChunkSpecification;
Expand Down Expand Up @@ -377,6 +379,37 @@ export function makeTemporaryChunk() {
{data: new Uint8Array(0), numPickIds: 0, typeToOffset, typeToIds, typeToIdMaps});
}

export function deserializeAnnotations(
serializedAnnotations: SerializedAnnotations,
rank: number, properties: Readonly<AnnotationPropertySpec>[]) {
const annotations: Annotation[] = [];
const annotationBuffer = serializedAnnotations.data;
let annotation: Annotation|undefined;
for (let [annotationType, annotationsOfType] of serializedAnnotations.typeToIdMaps.entries()) {
const handler = annotationTypeHandlers[annotationType as AnnotationType];
const numGeometryBytes = handler.serializedBytes(rank);
const baseOffset = annotationBuffer.byteOffset;
const dataView = new DataView(annotationBuffer.buffer);
const isLittleEndian = Endianness.LITTLE === ENDIANNESS;
const annotationPropertySerializer =
new AnnotationPropertySerializer(rank, numGeometryBytes, properties);
const annotationCount = annotationsOfType.size;
for (const [annotationId, annotationIndex] of annotationsOfType) {
annotation = handler.deserialize(
dataView,
baseOffset +
annotationPropertySerializer.propertyGroupBytes[0] *
annotationIndex,
isLittleEndian, rank, annotationId);
annotationPropertySerializer.deserialize(
dataView, baseOffset, annotationIndex, annotationCount, isLittleEndian,
annotation.properties = new Array(properties.length));
annotations.push(annotation);
}
}
return annotations;
}

export class MultiscaleAnnotationSource extends SharedObject implements
MultiscaleSliceViewChunkSource<AnnotationGeometryChunkSource>, AnnotationSourceSignals {
OPTIONS: {};
Expand Down Expand Up @@ -409,6 +442,44 @@ export class MultiscaleAnnotationSource extends SharedObject implements
}
}

activeAnnotations(state: AnnotationLayerState): Annotation[] {
const annotations: Annotation[] = [];
const {segmentFilteredSources, spatiallyIndexedSources, rank, properties, relationships} = this;
const {relationshipStates} = state.displayState;
let hasVisibleSegments = false;
for (let i = 0; i < relationships.length; i++) {
const relationship = relationships[i];
const state = relationshipStates.get(relationship)
if (state) {
const {showMatches: {value: showMatches}, segmentationState: {value: segmentationState}} = state;
if (!showMatches || !segmentationState) continue;
const chunks = segmentFilteredSources[i].chunks;
forEachVisibleSegment(segmentationState.segmentationGroupState.value, objectId => {
hasVisibleSegments = true;
const key = getObjectKey(objectId);
const chunk = chunks.get(key);
if (chunk !== undefined && chunk.state === ChunkState.GPU_MEMORY) {
const {data} = chunk;
if (data === undefined) return;
const {serializedAnnotations} = data;
annotations.push(...deserializeAnnotations(serializedAnnotations, rank, properties));
}
});
}
}
if (!hasVisibleSegments) {
for (const source of spatiallyIndexedSources) {
for (const [_key, chunk] of source.chunks) {
const {data} = chunk;
if (data === undefined) continue;
const {serializedAnnotations} = data;
annotations.push(...deserializeAnnotations(serializedAnnotations, rank, properties));
}
}
}
return annotations;
}

hasNonSerializedProperties() {
return this.relationships.length > 0;
}
Expand Down
11 changes: 10 additions & 1 deletion src/neuroglancer/annotation/type_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import {Annotation, AnnotationPropertySpec, AnnotationType, annotationTypeHandlers, getPropertyOffsets, propertyTypeDataType} from 'neuroglancer/annotation';
import {Annotation, AnnotationNumericPropertySpec, AnnotationPropertySpec, AnnotationType, annotationTypeHandlers, getPropertyOffsets, propertyTypeDataType} from 'neuroglancer/annotation';
import {AnnotationLayer} from 'neuroglancer/annotation/renderlayer';
import {PerspectiveViewRenderContext} from 'neuroglancer/perspective_view/render_layer';
import {ChunkDisplayTransformParameters} from 'neuroglancer/render_coordinate_transform';
Expand All @@ -29,6 +29,7 @@ import {ParameterizedContextDependentShaderGetter, parameterizedEmitterDependent
import {defineInvlerpShaderFunction, enableLerpShaderFunction} from 'neuroglancer/webgl/lerp';
import {ShaderBuilder, ShaderModule, ShaderProgram} from 'neuroglancer/webgl/shader';
import {addControlsToBuilder, setControlsInShader, ShaderControlsBuilderState, ShaderControlState} from 'neuroglancer/webgl/shader_ui_controls';
import {BasicHashColorShaderManager} from 'neuroglancer/segment_color';

const DEBUG_HISTOGRAMS = false;

Expand Down Expand Up @@ -197,6 +198,8 @@ export abstract class AnnotationRenderHelper extends AnnotationRenderHelperBase
pickIdsPerInstance: number;
targetIsSliceView: boolean;

protected hashColorShaderManager = new BasicHashColorShaderManager('hashColor');

constructor(
gl: GL, annotationType: AnnotationType, rank: number,
properties: readonly Readonly<AnnotationPropertySpec>[],
Expand Down Expand Up @@ -225,8 +228,14 @@ export abstract class AnnotationRenderHelper extends AnnotationRenderHelperBase
const referencedProperties: number[] = [];
const controlsReferencedProperties = parameters.referencedProperties;
const processedCode = parameters.parseResult.code;
this.hashColorShaderManager.defineShader(builder);
for (let i = 0, numProperties = properties.length; i < numProperties; ++i) {
const property = properties[i];
const enumLabels = (property as AnnotationNumericPropertySpec).enumLabels || [];
const enumValues = (property as AnnotationNumericPropertySpec).enumValues || [];
for (let i = 0; i < enumLabels.length && i < enumValues.length; i++) {
builder.addVertexCode(`#define prop_${property.identifier}_${enumLabels[i]} uint(${enumValues[i]})\n`);
}
const functionName = `prop_${property.identifier}`;
if (!controlsReferencedProperties.includes(property.identifier) &&
!processedCode.match(new RegExp(`\\b${functionName}\\b`))) {
Expand Down
16 changes: 16 additions & 0 deletions src/neuroglancer/annotation/user_layer.css
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,19 @@
content: "()";
color: #999;
}

.neuroglancer-annotation-shader-property-type:hover > .neuroglancer-annotation-shader-property-enum-labels,
.neuroglancer-annotation-shader-property-enum-labels:hover {
display: initial;
}

.neuroglancer-annotation-shader-property-enum-labels {
z-index: 100;
position: absolute;
background: black;
display: none;
user-select: text;
padding: 2px;
border: 1px solid white;
color: deepskyblue;
}
28 changes: 26 additions & 2 deletions src/neuroglancer/annotation/user_layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import './user_layer.css';

import {AnnotationPropertySpec, annotationPropertySpecsToJson, AnnotationType, LocalAnnotationSource, parseAnnotationPropertySpecs} from 'neuroglancer/annotation';
import {AnnotationNumericPropertySpec, AnnotationPropertySpec, annotationPropertySpecsToJson, AnnotationType, LocalAnnotationSource, parseAnnotationPropertySpecs} from 'neuroglancer/annotation';
import {AnnotationDisplayState, AnnotationLayerState} from 'neuroglancer/annotation/annotation_layer_state';
import {MultiscaleAnnotationSource} from 'neuroglancer/annotation/frontend_source';
import {CoordinateTransformSpecification, makeCoordinateSpace} from 'neuroglancer/coordinate_transform';
Expand Down Expand Up @@ -44,6 +44,9 @@ import {RenderScaleWidget} from 'neuroglancer/widget/render_scale_widget';
import {ShaderCodeWidget} from 'neuroglancer/widget/shader_code_widget';
import {registerLayerShaderControlsTool, ShaderControls} from 'neuroglancer/widget/shader_controls';
import {Tab} from 'neuroglancer/widget/tab_view';
import {getCssColor, SegmentColorHash} from 'neuroglancer/segment_color';
import {useWhiteBackground} from 'neuroglancer/util/color';
import {vec3} from 'neuroglancer/util/geom';

const POINTS_JSON_KEY = 'points';
const ANNOTATIONS_JSON_KEY = 'annotations';
Expand Down Expand Up @@ -572,6 +575,8 @@ class RenderingOptionsTab extends Tab {
codeWidget = this.registerDisposer(makeShaderCodeWidget(this.layer));
constructor(public layer: AnnotationUserLayer) {
super();
const segmentColorHash = SegmentColorHash.getDefault();
let tempColor = new Float32Array(3) as vec3;
const {element} = this;
element.classList.add('neuroglancer-annotation-rendering-tab');
element.appendChild(
Expand All @@ -588,7 +593,26 @@ class RenderingOptionsTab extends Tab {
div.classList.add('neuroglancer-annotation-shader-property');
const typeElement = document.createElement('span');
typeElement.classList.add('neuroglancer-annotation-shader-property-type');
typeElement.textContent = property.type;
const enumLabels = (property as AnnotationNumericPropertySpec).enumLabels;
const enumValues = (property as AnnotationNumericPropertySpec).enumValues;
if (enumLabels && enumValues) {
typeElement.textContent = 'enum';
const enumLabelsElement = document.createElement('div');
enumLabelsElement.classList.add('neuroglancer-annotation-shader-property-enum-labels');
for (let i = 0; i < enumLabels.length && i < enumValues.length; i++) {
const label = enumLabels[i];
const value = enumValues[i];
const labelElement = document.createElement('div');
labelElement.textContent = `prop_${property.identifier}_${label}`;
enumLabelsElement.appendChild(labelElement);
const color = segmentColorHash.computeNumber(tempColor, value);
labelElement.style.backgroundColor = getCssColor(color);
labelElement.style.color = useWhiteBackground(tempColor) ? 'white' : 'black';
}
typeElement.appendChild(enumLabelsElement);
} else {
typeElement.textContent = property.type;
}
const nameElement = document.createElement('span');
nameElement.classList.add('neuroglancer-annotation-shader-property-identifier');
nameElement.textContent = `prop_${property.identifier}`;
Expand Down
35 changes: 35 additions & 0 deletions src/neuroglancer/segment_color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,33 @@ import {Trackable} from './util/trackable';

const NUM_COMPONENTS = 2;

export class BasicHashColorShaderManager {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left this here for now in case we don't want to go ahead with this approach

constructor(public prefix: string) {}

defineShader(builder: ShaderBuilder) {
builder.addVertexCode(glsl_hashCombine);
builder.addVertexCode(glsl_hsvToRgb);
let s = `
vec3 ${this.prefix}(highp uint x) {
highp uint seed = 0u;
uint h = hashCombine(seed, x);
vec${NUM_COMPONENTS} v;
`;
for (let i = 0; i < NUM_COMPONENTS; ++i) {
s += `
v[${i}] = float(h & 0xFFu) / 255.0;
h >>= 8u;
`;
}
s += `
vec3 hsv = vec3(v.x, 0.5 + v.y * 0.5, 1.0);
return hsvToRgb(hsv);
}
`;
builder.addVertexCode(s);
}
}

export class SegmentColorShaderManager {
seedName = this.prefix + '_seed';

Expand Down Expand Up @@ -98,6 +125,14 @@ export class SegmentColorHash implements Trackable {
return out;
}

computeNumber(out: Float32Array, x: number) {
let h = hashCombine(this.hashSeed, x);
const c0 = (h & 0xFF) / 255;
const c1 = ((h >> 8) & 0xFF) / 255;
hsvToRgb(out, c0, 0.5 + 0.5 * c1, 1.0);
return out;
}

computeCssColor(x: Uint64) {
this.compute(tempColor, x);
return getCssColor(tempColor);
Expand Down
Loading
Loading