//#define USE_OPENGL_WORLD
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Diagnostics;
using Tao.OpenGl;
using Tao.FreeGlut;
using Tao.Platform.Windows;
using OpenGl.Gle;
using SharpImage.Rendering;
namespace SharpImage.Rendering
{
///
/// Attach this to the Metadata with the key "RenderingMethod.
/// If no method is specified, the default is Wireframe.
///
public enum siSpatialObjectRenderingMethod
{
Wireframe,
Surface
}
///
/// Attach this to the Metadata with the key "TubeRenderingMethod".
/// If no method is specified, the default is Disk.
///
public enum siTubeSpatialObjectRenderingMethod
{
Disk,
Sphere,
Cylinder,
PolyCone
}
///
/// A class for rendering spatial objects in the siVolumeRenderer.
///
public class siSpatialObjectRenderingHelper
{
///
/// A class for caching tube data.
///
private class siTubeCache
{
private Dictionary m_Points = new Dictionary();
private Dictionary m_Colors = new Dictionary();
private Dictionary m_Radii = new Dictionary();
public String AddTubeToCache(String key, siVolumeRenderer renderer, itk.itkDataObject root, itk.itkTubeSpatialObject tube)
{
// Get propertiers
itk.itkSize size = GetSizeFromRoot(root);
itk.itkSpacing spacing = GetSpacingFromRoot(root);
itk.itkArray modelsize = renderer.Properties.ModelSize;
itk.itkScalableAffineTransform transform = (tube as itk.itkSpatialObjectBase).ObjectToParentTransform as itk.itkScalableAffineTransform;
// Compute radius modifier
#if USE_OPENGL_WORLD
double radiusModifier = 1.0;
#else
double radiusModifier = ComputeTubeRadiusModifier(size, spacing, modelsize);
#endif
// Get the points
itk.itkTubeSpatialObjectPoint[] points = tube.GetPoints();
// Populate points, colors, and radii
int m = 0;
double[,] glePoints = new double[points.Length-1, 3];
float[,] gleColors = new float[points.Length-1, 3];
double[] gleRadii = new double[points.Length-1];
for (int n = 0; n < points.Length - 1; n++)
{
// Populate points
for (int i = 0; i < 3; i++)
{
#if USE_OPENGL_WORLD
glePoints[m, i] = points[n].Position[i];
#else
glePoints[m, i] = (points[n].Position[i] - transform.Offset[i]) * (modelsize[i] / size[i]);
#endif
}
// Populate colors
gleColors[m, 0] = (float)1.0F;
gleColors[m, 1] = (float)0.0F;
gleColors[m, 2] = (float)0.0F;
// Populate radii
#if USE_OPENGL_WORLD
gleRadii[m] = points[n].Radius;
#else
gleRadii[m] = points[n].Radius * radiusModifier;
#endif
m++;
}
// Set the number of slices/sides
Gle.gleSetNumSides(GetRenderingSlicesFromRoot(root));
// Cache data
this.m_Points.Add(key, glePoints);
this.m_Colors.Add(key, gleColors);
this.m_Radii.Add(key, gleRadii);
return key;
}
public bool IsInCache(String key)
{
return this.m_Points.ContainsKey(key);
}
public int GetNumberOfPoints(String key)
{
return this.m_Radii[key].Length;
}
public double[,] GetPoints(String key)
{
return this.m_Points[key];
}
public float[,] GetColors(String key)
{
return this.m_Colors[key];
}
public double[] GetRadii(String key)
{
return this.m_Radii[key];
}
}
private siTubeCache m_TubeCache = new siTubeCache();
private string m_TubeKey = String.Empty;
///
/// Return if the given input can be rendered by this helper class.
///
///
///
public bool CanRender(itk.itkDataObject input)
{
if (input is itk.itkSpatialObjectBase ||
input is siDataObjectDecorator)
return true;
else
return false;
}
///
/// Render the given input. CanRender() should have returned true on the input.
///
///
///
public void Render(siVolumeRenderer renderer, itk.itkDataObject input)
{
this.ResetTubeKey();
this.Render(renderer, input, null);
}
///
/// Render the given input. CanRender() should have returned true on the input.
///
///
///
/// The root/parent object.
private void Render(siVolumeRenderer renderer, itk.itkDataObject input, itk.itkDataObject root)
{
if (input is siDataObjectDecorator)
RenderScene(renderer, (input as siDataObjectDecorator), root);
else if (input is itk.itkGroupSpatialObject)
RenderGroup(renderer, input as itk.itkGroupSpatialObject, root);
else if (input is itk.itkBoxSpatialObject)
RenderBox(renderer, input as itk.itkBoxSpatialObject, root);
else if (input is itk.itkEllipseSpatialObject)
RenderEllipse(renderer, input as itk.itkEllipseSpatialObject, root);
else if (input is itk.itkTubeSpatialObject)
RenderTube(renderer, input as itk.itkTubeSpatialObject, root);
else
throw new NotSupportedException("The given spatial object is not supported for rendering: " + input.ToString());
}
private void RenderScene(siVolumeRenderer renderer, siDataObjectDecorator scene, itk.itkDataObject root)
{
itk.itkSpatialObjectBase[] children = scene.Object.GetObjects(0);
int i = 0;
foreach (itk.itkSpatialObjectBase child in children)
{
this.AppendToTubeKey("Scene", i);
Render(renderer, child, scene);
i++;
}
}
private void RenderGroup(siVolumeRenderer renderer, itk.itkGroupSpatialObject group, itk.itkDataObject root)
{
itk.itkSpatialObjectBase[] children = group.GetChildren(0);
int i = 0;
foreach (itk.itkSpatialObjectBase child in children)
{
this.AppendToTubeKey("Group", i);
Render(renderer, child, group);
i++;
}
}
private void RenderBox(siVolumeRenderer renderer, itk.itkBoxSpatialObject box, itk.itkDataObject root)
{
}
private void RenderEllipse(siVolumeRenderer renderer, itk.itkEllipseSpatialObject ellipse, itk.itkDataObject root)
{
// Start rendering
Gl.glMatrixMode(Gl.GL_MODELVIEW);
Gl.glPushMatrix();
{
bool isSphere = true;
for (int dim = 1; dim < ellipse.Radius.Length; dim++)
isSphere &= (ellipse.Radius[dim] == ellipse.Radius[dim - 1]);
if (!isSphere)
throw new NotSupportedException("The given ellipse is not supported: only spheres are currently supported.");
// Setup for rendering
Color color = Color.FromArgb(255, 0, 0, 0);
Gl.glColor4fv(siColorHelper.To4fv(color));
Glu.GLUquadric q = Glu.gluNewQuadric();
Glu.gluQuadricOrientation(q, Glu.GLU_OUTSIDE);
Glu.gluQuadricNormals(q, Glu.GLU_SMOOTH);
// Set the rendering method
siSpatialObjectRenderingMethod method = GetRenderingMethodFromRoot(root);
if (method == siSpatialObjectRenderingMethod.Wireframe)
Glu.gluQuadricDrawStyle(q, Glu.GLU_SILHOUETTE);
else if (method == siSpatialObjectRenderingMethod.Surface)
Glu.gluQuadricDrawStyle(q, Glu.GLU_FILL);
// Get properties
itk.itkScalableAffineTransform transform = (ellipse as itk.itkSpatialObjectBase).ObjectToParentTransform as itk.itkScalableAffineTransform;
itk.itkSize size = GetSizeFromRoot(root);
itk.itkSpacing spacing = GetSpacingFromRoot(root);
itk.itkArray modelsize = renderer.Properties.ModelSize;
// Translate from center of volume to corner
Gl.glTranslated(-0.5 * modelsize[0], -0.5 * modelsize[1], -0.5 * modelsize[2]);
// Translate to center of sphere
itk.itkPoint center = new itk.itkPoint((uint)ellipse.Radius.Length);
for (int i = 0; i < center.Dimension; i++)
center[i] = transform.Offset[i] * (modelsize[i] / size[i]);
Gl.glTranslated(center[0], center[1], center[2]);
// Render
double radius = ellipse.Radius[0] * ComputeTubeRadiusModifier(size, spacing, modelsize);
int numslices = GetRenderingSlicesFromRoot(root);
this.EllipseEnableLighting();
Glu.gluSphere(q, radius, numslices, numslices);
this.EllipseDisableLighting();
}
Gl.glPopMatrix();
}
private void RenderTube(siVolumeRenderer renderer, itk.itkTubeSpatialObject tube, itk.itkDataObject root)
{
// Start rendering
Gl.glMatrixMode(Gl.GL_MODELVIEW);
Gl.glPushMatrix();
{
// Setup point and line sizes
if (GetTubeRenderingMethodFromRoot(root) == siTubeSpatialObjectRenderingMethod.Disk)
{
Gl.glPointSize(2.2F);
Gl.glLineWidth(0.5F);
}
// Translate and scale
#if USE_OPENGL_WORLD
// Translate
itk.itkSize size = GetSizeFromRoot(root);
itk.itkSpacing spacing = GetSpacingFromRoot(root);
Gl.glScaled(spacing[0] / size[0], spacing[1] / size[1], spacing[2] / size[2]);
// Translate to offset
itk.itkScalableAffineTransform transform = (tube as itk.itkSpatialObjectBase).ObjectToParentTransform as itk.itkScalableAffineTransform;
Gl.glTranslated(transform.Offset[0], transform.Offset[1], transform.Offset[2]);
#else
// Translate
double trans0 = -1.0 * renderer.Properties.ModelSize[0] * 0.5;
double trans1 = -1.0 * renderer.Properties.ModelSize[1] * 0.5;
double trans2 = -1.0 * renderer.Properties.ModelSize[2] * 0.5;
Gl.glTranslated(trans0, trans1, trans2);
#endif
// Choose the rendering method
switch (GetTubeRenderingMethodFromRoot(root))
{
case siTubeSpatialObjectRenderingMethod.Disk:
case siTubeSpatialObjectRenderingMethod.Sphere:
case siTubeSpatialObjectRenderingMethod.Cylinder:
this.RenderTubeUsingGLU(renderer, tube, root);
break;
case siTubeSpatialObjectRenderingMethod.PolyCone:
this.RenderTubeUsingGLE(renderer, tube, root);
break;
}
}
Gl.glPopMatrix();
}
private void RenderTubeUsingGLU(siVolumeRenderer renderer, itk.itkTubeSpatialObject tube, itk.itkDataObject root)
{
itk.itkTubeSpatialObjectPoint[] points = tube.GetPoints();
for (int i=0; i 0)
previous = points[i - 1];
if (i < points.Length - 1)
next = points[i + 1];
// Render point
this.RenderTubePointUsingGLU(renderer, tube, root, points[i], previous, next);
//Pop
Gl.glPopMatrix();
}
}
private void RenderTubePointUsingGLU(siVolumeRenderer renderer,
itk.itkTubeSpatialObject tube,
itk.itkDataObject root,
itk.itkTubeSpatialObjectPoint currentSO,
itk.itkTubeSpatialObjectPoint previousSO,
itk.itkTubeSpatialObjectPoint nextSO)
{
// Get properties
siSpatialObjectRenderingMethod method = GetRenderingMethodFromRoot(root);
siTubeSpatialObjectRenderingMethod methodTube = GetTubeRenderingMethodFromRoot(root);
int numslices = GetRenderingSlicesFromRoot(root);
itk.itkScalableAffineTransform transform = (tube as itk.itkSpatialObjectBase).ObjectToParentTransform as itk.itkScalableAffineTransform;
itk.itkSize size = GetSizeFromRoot(root);
itk.itkSpacing spacing = GetSpacingFromRoot(root);
itk.itkArray modelsize = renderer.Properties.ModelSize;
// Convert physical positions to model positions
itk.itkPoint current = new itk.itkPoint(currentSO.Position.Dimension);
itk.itkPoint previous = new itk.itkPoint(currentSO.Position.Dimension);
itk.itkPoint next = new itk.itkPoint(currentSO.Position.Dimension);
for (int i = 0; i < current.Dimension; i++)
{
current[i] = (currentSO.Position[i] - transform.Offset[i]) * (modelsize[i] / size[i]);
if (previousSO != null)
previous[i] = (previousSO.Position[i] - transform.Offset[i]) * (modelsize[i] / size[i]);
if (nextSO != null)
next[i] = (nextSO.Position[i] - transform.Offset[i]) * (modelsize[i] / size[i]);
}
if (previousSO == null)
previous = null;
if (nextSO == null)
next = null;
// Compute radius modifier
double radiusModifier = ComputeTubeRadiusModifier(size, spacing, modelsize);
// Compute radii
double radiusCurrent = currentSO.Radius * radiusModifier;
double radiusNext = 0.0;
if (nextSO != null)
radiusNext = nextSO.Radius * radiusModifier;
// Render geometry
Gl.glDisable(Gl.GL_LIGHTING);
{
// Render line and dot
if (methodTube == siTubeSpatialObjectRenderingMethod.Disk)
{
// Render line between this and next
if (next != null)
{
Gl.glBegin(Gl.GL_LINES);
{
Gl.glColor4fv(siColorHelper.To4fv(Color.DarkGreen));
Gl.glVertex3dv(current.Data);
Gl.glVertex3dv(next.Data);
}
Gl.glEnd();
}
//Render center dot
Gl.glBegin(Gl.GL_POINTS);
{
Gl.glColor4fv(siColorHelper.To4fv(Color.DarkRed));
Gl.glVertex3d(current[0], current[1], current[2]);
}
Gl.glEnd();
}
}
// Translate
Gl.glTranslated(current[0], current[1], current[2]);
// Rotate
itk.itkVector f = new itk.itkVector(currentSO.Tangent);
f.Normalize();
itk.itkVector up = new itk.itkVector(0.0, 1.0, 0.0);
if (up == f)
up = new itk.itkVector(1.0, 0.0, 0.0);
itk.itkVector s = itk.itkVector.Cross(f, up);
s.Normalize();
itk.itkVector u = itk.itkVector.Cross(s, f);
u.Normalize();
double[] m = new double[16];
m[00] = s[0]; m[04] = u[0]; m[08] = f[0]; m[12] = 0;
m[01] = s[1]; m[05] = u[1]; m[09] = f[1]; m[13] = 0;
m[02] = s[2]; m[06] = u[2]; m[10] = f[2]; m[14] = 0;
m[03] = 0; m[07] = 0; m[11] = 0; m[15] = 1;
Gl.glMultMatrixd(m);
// Enable lighting
this.TubeEnableLighting(methodTube);
// Render tube segment
Color colorDisk = Color.FromArgb(255, 0, 0, 0);
Gl.glColor4fv(siColorHelper.To4fv(colorDisk));
Glu.GLUquadric q = Glu.gluNewQuadric();
Glu.gluQuadricOrientation(q, Glu.GLU_OUTSIDE);
Glu.gluQuadricNormals(q, Glu.GLU_SMOOTH);
if (method == siSpatialObjectRenderingMethod.Wireframe)
Glu.gluQuadricDrawStyle(q, Glu.GLU_SILHOUETTE);
else if (method == siSpatialObjectRenderingMethod.Surface)
Glu.gluQuadricDrawStyle(q, Glu.GLU_FILL);
if (methodTube == siTubeSpatialObjectRenderingMethod.Disk)
Glu.gluDisk(q, 0, radiusCurrent, numslices, numslices);
else if (methodTube == siTubeSpatialObjectRenderingMethod.Sphere)
Glu.gluSphere(q, radiusCurrent, numslices, numslices);
else if (methodTube == siTubeSpatialObjectRenderingMethod.Cylinder && next != null)
{
double height = current.EuclideanDistanceTo(next);
Glu.gluCylinder(q, radiusCurrent, radiusNext, height, numslices, numslices);
}
// Disable lighting
this.TubeDisableLighting(methodTube);
}
private void RenderTubeUsingGLE(siVolumeRenderer renderer, itk.itkTubeSpatialObject tube, itk.itkDataObject root)
{
// Set the method
siSpatialObjectRenderingMethod method = GetRenderingMethodFromRoot(root);
if (method == siSpatialObjectRenderingMethod.Wireframe)
Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_LINE);
else if (method == siSpatialObjectRenderingMethod.Surface)
Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_FILL);
// Get the tube data from the cache
String key = String.Empty;
if (this.m_TubeCache.IsInCache(this.m_TubeKey))
key = this.m_TubeKey;
else
key = this.m_TubeCache.AddTubeToCache(this.m_TubeKey, renderer, root, tube);
// Render the data
siTubeSpatialObjectRenderingMethod methodTube = GetTubeRenderingMethodFromRoot(root);
this.TubeEnableLighting(methodTube);
Gle.glePolyCone(this.m_TubeCache.GetNumberOfPoints(key),
this.m_TubeCache.GetPoints(key),
this.m_TubeCache.GetColors(key),
this.m_TubeCache.GetRadii(key));
this.TubeDisableLighting(methodTube);
}
private void TubeEnableLighting(siTubeSpatialObjectRenderingMethod method)
{
switch (method)
{
case siTubeSpatialObjectRenderingMethod.Sphere:
case siTubeSpatialObjectRenderingMethod.Cylinder:
case siTubeSpatialObjectRenderingMethod.PolyCone:
Gl.glPushMatrix();
Gl.glLoadIdentity();
{
// Setup Lighting
float[] lightColor = { 1.0F, 0.2F, 0.2F };
float[] lightPosition = { 0.0f, 0.0f, -2.0f, 1.0f };
Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_AMBIENT, lightColor);
Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_DIFFUSE, lightColor);
Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_SPECULAR, lightColor);
Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_POSITION, lightPosition);
}
Gl.glPopMatrix();
Gl.glEnable(Gl.GL_NORMALIZE);
Gl.glEnable(Gl.GL_LIGHT1);
Gl.glEnable(Gl.GL_LIGHTING);
break;
case siTubeSpatialObjectRenderingMethod.Disk:
default:
//Do nothing
break;
}
}
private void TubeDisableLighting(siTubeSpatialObjectRenderingMethod method)
{
switch (method)
{
case siTubeSpatialObjectRenderingMethod.Sphere:
case siTubeSpatialObjectRenderingMethod.Cylinder:
case siTubeSpatialObjectRenderingMethod.PolyCone:
Gl.glDisable(Gl.GL_LIGHTING);
Gl.glDisable(Gl.GL_LIGHT1);
break;
case siTubeSpatialObjectRenderingMethod.Disk:
default:
//Do nothing
break;
}
}
private void EllipseEnableLighting()
{
Gl.glPushMatrix();
Gl.glLoadIdentity();
{
// Setup Lighting
float[] lightColor = { 1.0F, 0.2F, 0.2F };
float[] lightPosition = { 0.0f, 0.0f, -2.0f, 1.0f };
Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_AMBIENT, lightColor);
Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_DIFFUSE, lightColor);
Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_SPECULAR, lightColor);
Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_POSITION, lightPosition);
}
Gl.glPopMatrix();
Gl.glEnable(Gl.GL_NORMALIZE);
Gl.glEnable(Gl.GL_LIGHT1);
Gl.glEnable(Gl.GL_LIGHTING);
}
private void EllipseDisableLighting()
{
Gl.glDisable(Gl.GL_LIGHTING);
Gl.glDisable(Gl.GL_LIGHT1);
}
internal static itk.itkSize GetSizeFromRoot(itk.itkDataObject root)
{
if (root is itk.itkSpatialObjectBase)
return (root as itk.itkSpatialObjectBase).LargestPossibleRegion.Size;
else if (root is siDataObjectDecorator)
return (root as siDataObjectDecorator).Size;
else
throw new NotSupportedException("The given parent spatial object is not supported: it does not contain size information.");
}
internal static itk.itkSpacing GetSpacingFromRoot(itk.itkDataObject root)
{
if (root is itk.itkSpatialObjectBase)
return (root as itk.itkSpatialObjectBase).Spacing;
else if (root is siDataObjectDecorator)
return (root as siDataObjectDecorator).Spacing;
else
throw new NotSupportedException("The given parent spatial object is not supported: it does not contain spacing information.");
}
internal static siSpatialObjectRenderingMethod GetRenderingMethodFromRoot(itk.itkDataObject root)
{
siSpatialObjectRenderingMethod method = siSpatialObjectRenderingMethod.Surface;
if (root.Metadata.ContainsKey("RenderingMethod"))
method = (siSpatialObjectRenderingMethod)root.Metadata["RenderingMethod"];
return method;
}
internal static siTubeSpatialObjectRenderingMethod GetTubeRenderingMethodFromRoot(itk.itkDataObject root)
{
siTubeSpatialObjectRenderingMethod method = siTubeSpatialObjectRenderingMethod.PolyCone;
if (root.Metadata.ContainsKey("TubeRenderingMethod"))
method = (siTubeSpatialObjectRenderingMethod)root.Metadata["TubeRenderingMethod"];
return method;
}
internal static int GetRenderingSlicesFromRoot(itk.itkDataObject root)
{
int slices = 50;
if (root.Metadata.ContainsKey("RenderingSlices"))
slices = (int)root.Metadata["RenderingSlices"];
return slices;
}
internal static double ComputeTubeRadiusModifier(itk.itkSize size, itk.itkSpacing spacing, itk.itkArray modelsize)
{
double result = 1.0;
if (spacing[0] == spacing[1])
result = modelsize[0] / size[0];
else if (spacing[1] == spacing[2])
result = modelsize[1] / size[1];
else if (spacing[0] == spacing[2])
result = modelsize[0] / size[0];
else
throw new NotSupportedException("The spacing is not supported: at least two dimensions must have the same spacing.");
return result;
}
private void ResetTubeKey()
{
this.m_TubeKey = "Tube";
}
private void AppendToTubeKey(String group, int index)
{
this.m_TubeKey += "_" + group + index.ToString();
}
}
}