Skip to content

Commit

Permalink
feat(annotation): added hashColor function that returns a RGB for a g…
Browse files Browse the repository at this point in the history
…iven value. Exposed annotation propery enum labels in shader as an optional replacement to the enum value. Added UI that shows the list of enum labels colored using it's hashColor.
  • Loading branch information
chrisj committed Sep 28, 2023
1 parent cee0146 commit 8398ac5
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 3 deletions.
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 {
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

0 comments on commit 8398ac5

Please sign in to comment.