Skip to content

feat: allow local annotations geometry to be editable in the UI via exact co-ordinate input from keyboard #811

@seankmartin

Description

@seankmartin

See #809 for idea from @stuarteberg. Opened issue here just to keep track. I think something like the below is probably in the right direction

Screencast.from.2025-07-21.10-47-11.mp4

For example, changing the visitTransformedAnnotationGeometry call in src/ui/annotations.ts to something like this would allow changing point annotations in the UI

visitTransformedAnnotationGeometry(
  annotation,
  chunkTransform as ChunkTransformParameters,
  (layerPosition, isVector) => {
    const copyButton = makeCopyButton({
      title: "Copy position",
      onClick: () => {
        setClipboard(layerPosition.map((x) => Math.floor(x)).join(", "));
      },
    });
    copyButton.style.gridColumn = "copy";
    positionGrid.appendChild(copyButton);
    for (let layerDim = 0; layerDim < layerRank; ++layerDim) {
      const coordElement = document.createElement("input");
      coordElement.classList.add(
        "neuroglancer-selected-annotation-details-position-coord",
      );
      coordElement.style.gridColumn = `coord ${layerDim + 1}`;
      coordElement.value = Math.floor(layerPosition[layerDim]).toString();
      coordElement.addEventListener("change", (event) => {
        const input = event.target as HTMLInputElement;
        const value = parseFloat(input.value);
        if (isNaN(value)) return;
        const newLayerPosition = new Float32Array(layerPosition.length);
        for (let i = 0; i < layerPosition.length; ++i) {
          newLayerPosition[i] = layerPosition[i];
        }
        newLayerPosition[layerDim] = value;
        const newAnnotation = reference.value;
        if (!newAnnotation) return;
        if (newAnnotation.type === AnnotationType.POINT) {
          // TODO consider to refactor the visitTransformedAnnotationGeometry
          // This is basically the same operation but using the inverse transform
          // Also to handle other annotation types
          // Annotations that are not points are more complex
          // because they need to know which piece of geometry
          // is being updated
          const toChunkTransform = (chunkTransform as ChunkTransformParameters)
            .layerToChunkTransform;
          const { layerRank } = chunkTransform as ChunkTransformParameters;
          const paddedChunkPosition = new Float32Array(layerRank);
          paddedChunkPosition.set(newLayerPosition, 0);
          const layerSourcePosition = new Float32Array(layerRank);
          (isVector ? matrix.transformVector : matrix.transformPoint)(
            layerSourcePosition,
            toChunkTransform,
            layerRank + 1,
            paddedChunkPosition,
            layerRank,
          );
          newAnnotation.point = layerSourcePosition;
        }
        annotationLayer.source.update(reference, newAnnotation);
        annotationLayer.source.commit(reference);
      });
      positionGrid.appendChild(coordElement);
    }
    if (!isVector) {
      const moveButton = makeMoveToButton({
        title: "Move to position",
        onClick: () => {
          setLayerPosition(this, chunkTransform, layerPosition);
        },
      });
      moveButton.style.gridColumn = "move";
      positionGrid.appendChild(moveButton);
    }
  },
);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions