//#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(); } } }