Skip to content

Commit

Permalink
Slice renderer window: Display data value and measure distances. (#146)
Browse files Browse the repository at this point in the history
* Slice renderer window: Show selected point and measure distance.

* removed debug draw

* Measure dataset coordinate space distance in slice view.

* Set scale to 1,0 on raw datasets.

* Serialise dataset scale, and set default value to 1
  • Loading branch information
mlavik1 committed Jan 4, 2023
1 parent aa04af2 commit d43c3c5
Show file tree
Hide file tree
Showing 12 changed files with 570 additions and 31 deletions.
169 changes: 144 additions & 25 deletions Assets/Editor/SliceRenderingEditorWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,24 @@ namespace UnityVolumeRendering
public class SliceRenderingEditorWindow : EditorWindow
{
private int selectedPlaneIndex = -1;
private bool handleMouseMovement = false;
private bool mouseIsDown = false;
private Vector2 mousePressPosition;
private Vector2 prevMousePos;
private Vector2 measurePoint;

private Texture moveIconTexture;
private Texture inspectIconTexture;
private Texture measureIconTexture;

private InputMode inputMode;

private enum InputMode
{
Move,
Inspect,
Measure
}

public static void ShowWindow()
{
SliceRenderingEditorWindow wnd = new SliceRenderingEditorWindow();
Expand All @@ -19,11 +34,18 @@ public static void ShowWindow()
private void SetInitialPosition()
{
Rect rect = this.position;
rect.width = 800.0f;
rect.height = 500.0f;
rect.width = 600.0f;
rect.height = 600.0f;
this.position = rect;
}

private void Awake()
{
moveIconTexture = Resources.Load<Texture>("Icons/MoveIcon");
inspectIconTexture = Resources.Load<Texture>("Icons/InspectIcon");
measureIconTexture = Resources.Load<Texture>("Icons/MeasureIcon");
}

private void OnFocus()
{
// set selected plane as active GameObject in Hierarchy
Expand All @@ -41,78 +63,175 @@ private void OnGUI()
if (spawnedPlanes.Length > 0)
selectedPlaneIndex = selectedPlaneIndex % spawnedPlanes.Length;

float bgWidth = Mathf.Min(this.position.width - 20.0f, (this.position.height - 50.0f) * 2.0f);
Rect bgRect = new Rect(0.0f, 0.0f, bgWidth, bgWidth * 0.5f);
if (GUI.Toggle(new Rect(0.0f, 0.0f, 40.0f, 40.0f), inputMode == InputMode.Move, new GUIContent(moveIconTexture, "Move slice"), GUI.skin.button))
inputMode = InputMode.Move;
if (GUI.Toggle(new Rect(40.0f, 0.0f, 40.0f, 40.0f), inputMode == InputMode.Inspect, new GUIContent(inspectIconTexture, "Inspect values"), GUI.skin.button))
inputMode = InputMode.Inspect;
if (GUI.Toggle(new Rect(80.0f, 0.0f, 40.0f, 40.0f), inputMode == InputMode.Measure, new GUIContent(measureIconTexture, "Inspect values"), GUI.skin.button))
inputMode = InputMode.Measure;

Rect bgRect = new Rect(0.0f, 40.0f, 0.0f, 0.0f);

if (selectedPlaneIndex != -1 && spawnedPlanes.Length > 0)
{
SlicingPlane planeObj = spawnedPlanes[System.Math.Min(selectedPlaneIndex, spawnedPlanes.Length - 1)];
Vector3 planeScale = planeObj.transform.lossyScale;

float heightWidthRatio = planeScale.z / planeScale.x;
float bgWidth = Mathf.Min(this.position.width - 20.0f, (this.position.height - 50.0f) * 2.0f);
float bgHeight = Mathf.Min(bgWidth, this.position.height - 150.0f);
bgWidth = bgHeight / heightWidthRatio;
bgRect = new Rect(0.0f, 40.0f, bgWidth, bgHeight);

// Draw the slice view
Material mat = planeObj.GetComponent<MeshRenderer>().sharedMaterial;
Graphics.DrawTexture(bgRect, mat.GetTexture("_DataTex"), mat);

Vector2 relMousePos = Event.current.mousePosition - bgRect.position;
Vector2 relMousePosNormalised = relMousePos / new Vector2(bgRect.width, bgRect.height);

// Handle mouse click inside slice view (activates moving the plane with mouse)
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && bgRect.Contains(new Vector2(Event.current.mousePosition.x, Event.current.mousePosition.y)))
{
handleMouseMovement = true;
prevMousePos = Event.current.mousePosition;
mouseIsDown = true;
mousePressPosition = prevMousePos = relMousePosNormalised;
}

// Handle mouse movement (move the plane)
if (handleMouseMovement)
// Move the plane.
if (inputMode == InputMode.Move && mouseIsDown)
{
Vector2 mouseOffset = (Event.current.mousePosition - prevMousePos) / new Vector2(bgRect.width, bgRect.height);
Vector2 mouseOffset = relMousePosNormalised - prevMousePos;
if (Mathf.Abs(mouseOffset.y) > 0.00001f)
{
planeObj.transform.Translate(Vector3.up * mouseOffset.y);
prevMousePos = Event.current.mousePosition;
}
}
}
// Show value at mouse position.
else if (inputMode == InputMode.Inspect)
{
if (mouseIsDown)
measurePoint = relMousePosNormalised;
Vector3 worldSpacePoint = GetWorldPosition(measurePoint, planeObj);
float value = GetValueAtPosition(measurePoint, planeObj);
GUI.Label(new Rect(0.0f, bgRect.y + bgRect.height + 0.0f, 150.0f, 30.0f), $"Value: {value.ToString()}");
}
// Measure distance between two points.
else if (inputMode == InputMode.Measure)
{
if (mouseIsDown)
measurePoint = relMousePosNormalised;

Vector2 start = mousePressPosition;
Vector2 end = measurePoint;
// Convert to data coordinates
Vector3 startDataPos = GetDataPosition(start, planeObj);
Vector3 endDatapos = GetDataPosition(end, planeObj);

// Display distance
float distance = Vector3.Distance(startDataPos, endDatapos);
GUI.Label(new Rect(0.0f, bgRect.y + bgRect.height + 0.0f, 150.0f, 30.0f), $"Distance: {distance.ToString()}");

// Draw line
Vector2 lineStart = start * new Vector2(bgRect.width, bgRect.height) + new Vector2(bgRect.x, bgRect.y);
Vector2 lineEnd = end * new Vector2(bgRect.width, bgRect.height) + new Vector2(bgRect.x, bgRect.y);
Handles.BeginGUI();
Handles.color = Color.red;
Handles.DrawLine(lineStart, lineEnd);
Handles.EndGUI();
}

if (Event.current.type == EventType.MouseUp)
handleMouseMovement = false;
if (mouseIsDown)
prevMousePos = relMousePosNormalised;

if (Event.current.type == EventType.MouseUp)
mouseIsDown = false;
}

// Show buttons for changing the active plane
if (spawnedPlanes.Length > 0)
{
if (GUI.Button(new Rect(0.0f, bgRect.y + bgRect.height + 20.0f, 70.0f, 30.0f), "previous\nplane"))
GUI.Label(new Rect(0.0f, bgRect.y + bgRect.height + 20.0f, 450.0f, 20.0f), "Select a plane (previous / next).");

if (GUI.Button(new Rect(0.0f, bgRect.y + bgRect.height + 40.0f, 70.0f, 20.0f), "<"))
{
selectedPlaneIndex = (selectedPlaneIndex - 1) % spawnedPlanes.Length;
Selection.activeGameObject = spawnedPlanes[selectedPlaneIndex].gameObject;
}
if (GUI.Button(new Rect(90.0f, bgRect.y + bgRect.height + 20.0f, 70.0f, 30.0f), "next\nplane"))
if (GUI.Button(new Rect(90.0f, bgRect.y + bgRect.height + 40.0f, 70.0f, 20.0f), ">"))
{
selectedPlaneIndex = (selectedPlaneIndex + 1) % spawnedPlanes.Length;
Selection.activeGameObject = spawnedPlanes[selectedPlaneIndex].gameObject;
}
}

// Show button for adding new plane
if (GUI.Button(new Rect(180.0f, bgRect.y + bgRect.height + 20.0f, 70.0f, 30.0f), "add\nplane"))
VolumeRenderedObject volRend = FindObjectOfType<VolumeRenderedObject>();
if (volRend != null)
{
VolumeRenderedObject volRend = FindObjectOfType<VolumeRenderedObject>();
if (volRend != null)
if (GUI.Button(new Rect(200.0f, bgRect.y + bgRect.height + 0.0f, 120.0f, 20.0f), "Create XY plane"))
{
selectedPlaneIndex = spawnedPlanes.Length;
volRend.CreateSlicingPlane();
}
else if (GUI.Button(new Rect(200.0f, bgRect.y + bgRect.height + 20.0f, 120.0f, 20.0f), "Create XZ plane"))
{
selectedPlaneIndex = spawnedPlanes.Length;
SlicingPlane plane = volRend.CreateSlicingPlane();
plane.transform.localRotation = Quaternion.Euler(90.0f, 0.0f, 0.0f);
}
else if (GUI.Button(new Rect(200.0f, bgRect.y + bgRect.height + 40.0f, 120.0f, 20.0f), "Create ZY plane"))
{
selectedPlaneIndex = spawnedPlanes.Length;
SlicingPlane plane = volRend.CreateSlicingPlane();
plane.transform.localRotation = Quaternion.Euler(0.0f, 0.0f, 90.0f);
}
}

// Show button for removing
if (spawnedPlanes.Length > 0 && GUI.Button(new Rect(270.0f, bgRect.y + bgRect.height + 20.0f, 70.0f, 30.0f), "remove\nplane"))
if (spawnedPlanes.Length > 0 && GUI.Button(new Rect(320.0f, bgRect.y + bgRect.height + 20.0f, 70.0f, 30.0f), "remove\nplane"))
{
SlicingPlane planeToRemove = spawnedPlanes[selectedPlaneIndex];
GameObject.DestroyImmediate(planeToRemove.gameObject);
}

// Show hint
if (spawnedPlanes.Length > 0)
GUI.Label(new Rect(0.0f, bgRect.y + bgRect.height + 60.0f, 450.0f, 30.0f), "Move plane by left clicking in the above view and dragging the mouse,\n or simply move it in the object hierarchy.");
if (inputMode == InputMode.Move && spawnedPlanes.Length > 0)
GUI.Label(new Rect(0.0f, bgRect.y + bgRect.height + 70.0f, 450.0f, 30.0f), "Move plane by left clicking in the above view and dragging the mouse,\n or simply move it in the object hierarchy.");
else if (inputMode == InputMode.Inspect)
GUI.Label(new Rect(0.0f, bgRect.y + bgRect.height + 70.0f, 450.0f, 30.0f), "Click somewhere to display the data value at a location.");
else if (inputMode == InputMode.Measure)
GUI.Label(new Rect(0.0f, bgRect.y + bgRect.height + 70.0f, 450.0f, 30.0f), "Click and drag to measure the distance between two points.");
}

public void OnInspectorUpdate()
{
Repaint();
}

private Vector3 GetWorldPosition(Vector2 relativeMousePosition, SlicingPlane slicingPlane)
{
Vector3 planePoint = new Vector3(0.5f - relativeMousePosition.x, 0.0f, relativeMousePosition.y - 0.5f) * 10.0f;
return slicingPlane.transform.TransformPoint(planePoint);
}

private Vector3 GetDataPosition(Vector2 relativeMousePosition, SlicingPlane slicingPlane)
{
Vector3 worldSpacePosition = GetWorldPosition(relativeMousePosition, slicingPlane);
Vector3 objSpacePoint = slicingPlane.targetObject.transform.InverseTransformPoint(worldSpacePosition);
Vector3 uvw = objSpacePoint + Vector3.one * 0.5f;
VolumeDataset dataset = slicingPlane.targetObject.dataset;
return new Vector3(uvw.x * dataset.scaleX, uvw.y * dataset.scaleY,uvw.z * dataset.scaleZ);
}

private float GetValueAtPosition(Vector2 relativeMousePosition, SlicingPlane slicingPlane)
{
Vector3 worldSpacePosition = GetWorldPosition(relativeMousePosition, slicingPlane);
Vector3 objSpacePoint = slicingPlane.targetObject.transform.InverseTransformPoint(worldSpacePosition);
VolumeDataset dataset = slicingPlane.targetObject.dataset;
// Convert to texture coordinates.
Vector3 uvw = objSpacePoint + Vector3.one * 0.5f;
// Look up data value at current position.
Vector3Int index = new Vector3Int((int)(uvw.x * dataset.dimX), (int)(uvw.y * dataset.dimY), (int)(uvw.z * dataset.dimZ));
return dataset.GetData(index.x, index.y, index.z);
}

}
}
8 changes: 8 additions & 0 deletions Assets/Resources/Icons.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added Assets/Resources/Icons/InspectIcon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
135 changes: 135 additions & 0 deletions Assets/Resources/Icons/InspectIcon.png.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added Assets/Resources/Icons/MeasureIcon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit d43c3c5

Please sign in to comment.