/*=============================================================================
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
}
}