/*============================================================================= Project: SharpImage Module: siTubeSpatialObject.cs Language: C# Author: Dan Mueller Date: $Date: 2007-07-06 10:57:00 +1000 (Fri, 06 Jul 2007) $ Revision: $Revision: 2 $ Copyright (c) Queensland University of Technology (QUT) 2007. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =============================================================================*/ 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.SpatialObject { /// /// Attach this to the siSpatialObject.Metadata with the key "TubeRenderingMethod". /// If no method is specified, the default is Disk. /// public enum siTubeSpatialObjectRenderingMethod { Disk, Sphere, Cylinder, PolyCone } /// /// Attach this to the siSpatialObject.Metadata with the key "TubeRenderingStyle". /// If no method is specified, the default is Wireframe. /// public enum siTubeSpatialObjectRenderingStyle { Wireframe, Surface } public class siTubeSpatialObject : siSpatialObject { #region Constants //===================================================================== private const string MET_TUBE_OBJECT_TYPE = MET_OBJECT_TYPE + "Tube"; private const string MET_PARENT_ID = "ParentID = "; private const string MET_COLOR = "Color = "; private const string MET_TRANSFORM_MATRIX = "TransformMatrix = "; private const string MET_OFFSET = "Offset = "; private const string MET_CENTER_OF_ROTATION = "CenterOfRotation = "; private const string MET_ELEMENT_SPACING = "ElementSpacing = "; private const string MET_ROOT = "Root = "; private const string MET_POINT_DIM = "PointDim = "; private const string MET_NUM_POINTS = "NPoints = "; private const string MET_POINTS = "Points = "; //===================================================================== #endregion #region Instance Variables //===================================================================== private double[,] glePoints; private float[,] gleColors; private double[] gleRadii; //===================================================================== #endregion #region Construction and Disposal //===================================================================== /// /// Protected constructor. /// protected siTubeSpatialObject() { } /// /// Static factory constructor. /// /// public static siTubeSpatialObject New() { return new siTubeSpatialObject(); } /// /// Parses an siTubeSpatialObject from a collection of metafile lines. /// /// /// public static siTubeSpatialObject Parse(string[] lines) { try { // Create tube siTubeSpatialObject tube = new siTubeSpatialObject(); // Read header info string sNumDims = lines[0].Replace(MET_NUM_DIMS, ""); string sParentID = lines[1].Replace(MET_PARENT_ID, ""); string sColor = lines[2].Replace(MET_COLOR, ""); tube.m_TransformMatrixString = lines[3].Replace(MET_TRANSFORM_MATRIX, ""); string sOffset = lines[4].Replace(MET_OFFSET, ""); string sCenterOfRotation = lines[5].Replace(MET_CENTER_OF_ROTATION, ""); string sElementSpacing = lines[6].Replace(MET_ELEMENT_SPACING, ""); string sRoot = lines[7].Replace(MET_ROOT, ""); string sPointDim = lines[8].Replace(MET_POINT_DIM, ""); string sNumPoints = lines[9].Replace(MET_NUM_POINTS, ""); string sPointsHeader = lines[10]; // Parse header info tube.m_Root = (sRoot == "True") ? true : false; int numPoints = Int32.Parse(sNumPoints); // Parse each point for (int i = 0; i < numPoints; i++) { siTubeSpatialObjectPoint point = siTubeSpatialObjectPoint.Parse(lines[i + 11]); if (point != null) tube.Points.Add(point); } // Return if (tube != null && tube.Points.Count == 0) return null; else return tube; } catch { return null; } } //===================================================================== #endregion #region Properties //===================================================================== #region Root //===================================================================== private bool m_Root = false; /// /// Gets if this tube is the root of a tube network. /// public bool Root { get { return this.m_Root; } } //===================================================================== #endregion #region TransformMatrixString //===================================================================== private string m_TransformMatrixString = string.Empty; /// /// Gets the TransformMatrix string read from the metafile. /// public string TransformMatrixString { get { return this.m_TransformMatrixString; } } //===================================================================== #endregion #region Offset //===================================================================== private itk.itkOffset m_Offset = new itk.itkOffset(siSpatialObject.Dimension); /// /// Gets the offset applied to the whole TubeSpatialObject. /// public itk.itkOffset Offset { get { return this.m_Offset; } } //===================================================================== #endregion #region CenterOfRotation //===================================================================== private itk.itkPoint m_CenterOfRotation = new itk.itkPoint(siSpatialObject.Dimension); /// /// Gets the CenterOfRotation applied to the TransformMatrix. /// public itk.itkPoint CenterOfRotation { get { return this.m_CenterOfRotation; } } //===================================================================== #endregion #region Points //===================================================================== private List m_Points = new List(); /// /// Gets the list of points comprising this tube. /// public List Points { get { return this.m_Points; } } //===================================================================== #endregion #region Rendering Method and Style //===================================================================== /// /// Gets the rendering method. /// If no method exists, the default (Disk) is returned. /// public siTubeSpatialObjectRenderingMethod RenderingMethod { get { siTubeSpatialObjectRenderingMethod method = siTubeSpatialObjectRenderingMethod.Disk; if (this.Metadata.ContainsKey("TubeRenderingMethod")) method = (siTubeSpatialObjectRenderingMethod)this.Metadata["TubeRenderingMethod"]; return method; } } /// /// Gets the rendering style. /// If no style exists, the default (Surface) is returned. /// public siTubeSpatialObjectRenderingStyle RenderingStyle { get { siTubeSpatialObjectRenderingStyle style = siTubeSpatialObjectRenderingStyle.Surface; if (this.Metadata.ContainsKey("TubeRenderingStyle")) style = (siTubeSpatialObjectRenderingStyle)this.Metadata["TubeRenderingStyle"]; return style; } } /// /// Gets the number of slices to render. /// If no number slices exists, the default (50) is returned. /// public int RenderingSlices { get { int slices = 50; if (this.Metadata.ContainsKey("TubeRenderingSlices")) slices = (int)this.Metadata["TubeRenderingSlices"]; return slices; } } //===================================================================== #endregion //===================================================================== #endregion #region Public Methods //===================================================================== /// /// Initialises the spatial object for rendering. /// public override void Initialise(siRenderer renderer) { if (this.RenderingMethod == siTubeSpatialObjectRenderingMethod.PolyCone) { // Get model size itk.itkArray modelSize = renderer.Metadata["ModelSize"] as itk.itkArray; // Compute radius modifier double radiusModifier = 1.0; if (this.Spacing[0] == this.Spacing[1]) radiusModifier = modelSize[0] / this.Size[0]; else if (this.Spacing[1] == this.Spacing[2]) radiusModifier = modelSize[1] / this.Size[1]; else if (this.Spacing[0] == this.Spacing[2]) radiusModifier = modelSize[0] / this.Size[0]; // Find min/max radius double radiusMin = +10000.0; double radiusMax = -10000.0; for (int n = 0; n < this.Points.Count; n++) { if (this.m_Points[n].Radius < radiusMin) radiusMin = this.m_Points[n].Radius; if (this.m_Points[n].Radius > radiusMax) radiusMax = this.m_Points[n].Radius; } // Create LUT siLookupTable lut = new siLookupTable(); lut.SetTableRange(radiusMin, radiusMax); lut.SetTableToHueRange(0.0, 0.3334); // Populate points, colors, and radii glePoints = new double[this.Points.Count, 3]; gleColors = new float[this.Points.Count, 3]; gleRadii = new double[this.Points.Count]; for (int n = 0; n < this.Points.Count; n++) { // Populate points for (int i = 0; i < 3; i++) glePoints[n,i] = (this.m_Points[n].Position[i] - this.Origin[i]) * (modelSize[i] / this.Size[i]); // Populate colors siLookupTableEntry entry = lut[this.m_Points[n].Radius]; gleColors[n, 0] = (float)entry.R; gleColors[n, 1] = (float)entry.G; gleColors[n, 2] = (float)entry.B; // Populate radii gleRadii[n] = this.m_Points[n].Radius * radiusModifier; } // Set the number of slices/sides Gle.gleSetNumSides(this.RenderingSlices); } } /// /// Renders the spatial object using OpenGL. /// /// The renderer this spatial object is being renderered to. public override void Render(siRenderer renderer) { // Setup Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glPushMatrix(); { // Setup point and line sizes if (this.RenderingMethod == siTubeSpatialObjectRenderingMethod.Disk) { Gl.glPointSize(2.2F); Gl.glLineWidth(0.5F); } // Translate to origin Gl.glTranslated(-0.5, -0.5, -0.5); // Choose the rendering method switch (this.RenderingMethod) { case siTubeSpatialObjectRenderingMethod.Disk: case siTubeSpatialObjectRenderingMethod.Sphere: case siTubeSpatialObjectRenderingMethod.Cylinder: this.RenderUsingGLU(renderer); break; case siTubeSpatialObjectRenderingMethod.PolyCone: this.RenderUsingGLE(renderer); break; } } Gl.glPopMatrix(); } /// /// Return a string representation of the tube spatial object. /// /// public override string ToString() { string result = "TubeSpatialObject: NPoints = " + this.Points.Count.ToString() + Environment.NewLine; foreach (siTubeSpatialObjectPoint point in this.Points) result += point.ToString() + Environment.NewLine; result = result.TrimEnd('\r', '\n'); return result; } //===================================================================== #endregion #region Private Methods //===================================================================== private void RenderUsingGLE(siRenderer renderer) { this.EnableLighting(); if (this.RenderingStyle == siTubeSpatialObjectRenderingStyle.Wireframe) Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_LINE); else Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_FILL); Gle.glePolyCone(this.m_Points.Count, glePoints, gleColors, gleRadii); this.DisableLighting(); } private void RenderUsingGLU(siRenderer renderer) { int index = 0; foreach (siTubeSpatialObjectPoint point in this.Points) { //Push Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glPushMatrix(); // Get previous and next siTubeSpatialObjectPoint previous = null; siTubeSpatialObjectPoint next = null; if (index > 0) previous = this.Points[index - 1]; if (index < this.Points.Count - 1) next = this.Points[index + 1]; index += 1; // Render point this.RenderTubePointUsingGLU(renderer, point, previous, next); //Pop Gl.glPopMatrix(); } } /// /// Renders a single tube spatial object point using OpenGL.GLU. /// /// /// The current point to render. /// The previous point rendered. May be null. /// The next point to be rendered. May be null. private void RenderTubePointUsingGLU(siRenderer renderer, siTubeSpatialObjectPoint currentSO, siTubeSpatialObjectPoint previousSO, siTubeSpatialObjectPoint nextSO) { // Convert physical positions to model positions itk.itkArray modelSize = renderer.Metadata["ModelSize"] as itk.itkArray; 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] - this.Origin[i]) * (modelSize[i] / this.Size[i]); if (previousSO != null) previous[i] = (previousSO.Position[i] - this.Origin[i]) * (modelSize[i] / this.Size[i]); if (nextSO != null) next[i] = (nextSO.Position[i] - this.Origin[i]) * (modelSize[i] / this.Size[i]); } if (previousSO == null) previous = null; if (nextSO == null) next = null; // Compute radius modifier double radiusModifier = 1.0; if (this.Spacing[0] == this.Spacing[1]) radiusModifier = modelSize[0] / this.Size[0]; else if (this.Spacing[1] == this.Spacing[2]) radiusModifier = modelSize[1] / this.Size[1]; else if (this.Spacing[0] == this.Spacing[2]) radiusModifier = modelSize[0] / this.Size[0]; // 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 (this.RenderingMethod == 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.EnableLighting(); // 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 (this.RenderingStyle == siTubeSpatialObjectRenderingStyle.Wireframe) Glu.gluQuadricDrawStyle(q, Glu.GLU_SILHOUETTE); else if (this.RenderingStyle == siTubeSpatialObjectRenderingStyle.Surface) Glu.gluQuadricDrawStyle(q, Glu.GLU_FILL); if (this.RenderingMethod == siTubeSpatialObjectRenderingMethod.Disk) Glu.gluDisk(q, 0, radiusCurrent, this.RenderingSlices, this.RenderingSlices); else if (this.RenderingMethod == siTubeSpatialObjectRenderingMethod.Sphere) Glu.gluSphere(q, radiusCurrent, this.RenderingSlices, this.RenderingSlices); else if (this.RenderingMethod == siTubeSpatialObjectRenderingMethod.Cylinder && next != null) { double height = current.EuclideanDistanceTo(next); Glu.gluCylinder(q, radiusCurrent, radiusNext, height, this.RenderingSlices, this.RenderingSlices); } // Disable lighting this.DisableLighting(); } private void EnableLighting() { switch (this.RenderingMethod) { 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 DisableLighting() { switch (this.RenderingMethod) { 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; } } //===================================================================== #endregion } }