context: /*=============================================================================================================
  Copyright (C) 2006  Jordan Sparks, DMD.  http://www.open-dent.com,  http://www.docsparks.com
  
  This program is free software; you can redistribute it and/or modify it under the terms of the
  GNU Db Public License as published by the Free Software Foundation; either version 2 of the License,
  or (at your option) any later version.
  
  This program is distributed in the hope that it will be useful, but without any warranty. See the GNU Db Public License
  for more details, available at http://www.opensource.org/licenses/gpl-license.php
  
  Any changes to this program must follow the guidelines of the GPL license if a modified version is to be
  redistributed.
  ===============================================================================================================*/
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Collections.Specialized;
  using System.ComponentModel;
  using System.Data;
  using System.Diagnostics;
  using System.Drawing;
  using System.Drawing.Drawing2D;
  using System.Drawing.Imaging;
  using System.IO;
  using System.Text;
  using System.Windows.Forms;
  using CodeBase;
  using Tao.OpenGl;
  using Tao.Platform.Windows;
  using OpenDentBusiness;
  
  namespace SparksToothChart {
  	public partial class ToothChartOpenGL:CodeBase.OpenGLWinFormsControl{//.SimpleOpenGlControl {
  		float[] specular_color_normal;//white
  		float[] specular_color_cementum;//gray
  		float[] shininess;		
  		private bool MouseIsDown;
  		///<summary>Mouse move causes this variable to be updated with the current tooth that the mouse is hovering over.</summary>
  		private string hotTooth;
  		///<summary>The previous hotTooth.  If this is different than hotTooth, then mouse has just now moved to a new tooth.  Can be 0 to represent no previous.</summary>
  		private string hotToothOld;
  		private int fontOffset;
  		private int displayListOffset;
  		private string[][] fontsymbols;
  		//<summary>This gets set to true during certain operations where we do not need to redraw all the teeth.  Specifically, during tooth selection where only the color of the tooth number text needs to change.  In this case, the rest of the scene will not be rendered again.</summary>
  		//private bool suspendRendering;
  		private int selectedPixelFormat;
  		///<summary></summary>
  		[Category("Action"),Description("Occurs when the mouse goes up ending a drawing segment.")]
  		public event ToothChartDrawEventHandler SegmentDrawn=null;
  		///<summary>This is a reference to the TcData object that's at the wrapper level.</summary>
  		public ToothChartData TcData;
  		//<summary>GDI+ handle to this control. Used for line drawing and font measurement.</summary>
  		//private Graphics g=null;
  
  		///<summary>Specify the hardware mode to create the tooth chart with. Set hardwareMode=true to try for hardware accelerated graphics, and set hardwareMode=false to try and get software graphics.</summary>
  		public ToothChartOpenGL(bool hardwareMode,int preferredPixelFormatNum) {
  			usehardware=hardwareMode;
  			InitializeComponent();
  			this.TaoSetupContext += new System.EventHandler(ToothChart_TaoSetupContext);
  			this.TaoRenderScene += new System.EventHandler(ToothChart_TaoRenderScene);
  			selectedPixelFormat=TaoInitializeContexts(preferredPixelFormatNum);
  			TaoRenderEnabled=true;
  			Gl.glDisable(Gl.GL_TEXTURE);
  			//Disable texturing, since we don't use it.  This should prevent a glCopyPixels() problem in Gdi.SwapBuffersFast() on ATI graphics cards.
  			/* This didn't work because the OpenGL control is not designed to be a container.  I don't have time to wrap it in another layer.
  			pictBox=new PictureBox();
  			pictBox.Location=new Point(0,0);
  			pictBox.Name="pictBox";
  			pictBox.Size=this.Size;
  			pictBox.Dock=DockStyle.Fill;
  			pictBox.Image=bitmapInPictBox;
  			Controls.Add(pictBox);*/
  		}
  
  		public void InitializeGraphics() {
  			MakeDisplayLists();
  			//g=this.CreateGraphics();
  		}
  
  		protected override void OnResize(EventArgs e) {
  			base.OnResize(e);
  		}
  
  		private void ToothChart_TaoSetupContext(object sender, System.EventArgs e){//event from base class when context needs to be setup.
  			MakeRasterFont();
  		}
  
  		public int SelectedPixelFormatNumber {
  			get{
  				return selectedPixelFormat;
  			}
  		}
  
  		///<summary>Returns a bitmap of what is showing in the control.  Used for printing.</summary>
  		public Bitmap GetBitmap(){
  			//This doesn't seem to work sometimes
  			//return GetBitmapOfOpenGL();
  			//this seems convoluted
  			Bitmap dummy=new Bitmap(Width,Height);
  			Graphics g=Graphics.FromImage(dummy);
  			PaintEventArgs e=new PaintEventArgs(g,new Rectangle(0,0,Width,Height));
  			Render(e);
  			//Bitmap result=ReadFrontBuffer();
  			Bitmap result=GetBitmapOfOpenGL();
  			g.Dispose();
  			return result;
  		}
  
  		/// <summary>This only gets the 3D scene.</summary>
  		private Bitmap GetBitmapOfOpenGL(){
  			Gl.glFlush();
  			Bitmap bitmap = new Bitmap(Width,Height);
  			BitmapData bitmapData=bitmap.LockBits(new Rectangle(0,0,bitmap.Width,bitmap.Height),ImageLockMode.WriteOnly,PixelFormat.Format24bppRgb);
  			Gl.glReadPixels(0,0,bitmap.Width,bitmap.Height,Gl.GL_BGR_EXT,Gl.GL_UNSIGNED_BYTE,bitmapData.Scan0);
  			bitmap.UnlockBits(bitmapData);
  			bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
  			return bitmap; 
  		}
  
  		protected override void OnPaint(PaintEventArgs e) {
  			base.OnPaint(e);
  		}
  
  		private void ToothChart_TaoRenderScene(object sender, System.EventArgs e){
  			//if(suspendRendering){
  			//	return;
  			//}
  			//This first part was originally in setup context
  			Gl.glClearColor((float)TcData.ColorBackground.R/255f,(float)TcData.ColorBackground.G/255f,(float)TcData.ColorBackground.B/255f,0f);
  			Gl.glClearAccum(0f,0f,0f,0f);
  			//Lighting
  			float ambI=.2f;//.1f;//Darker for testing
  			float difI=.6f;//.3f;//Darker for testing
  			float specI=1f;
  			float[] light_ambient = new float[] { ambI,ambI,ambI,1f };//RGB,A=1 for no transparency. Default 0001
  			float[] light_diffuse = new float[] { difI,difI,difI,1f };//RGBA. Default 1111. 'typical' 
  			float[] light_specular = new float[] { specI,specI,specI,1f };//RGBA. Default 1111
  			float[] light_position = new float[] { -0.5f,0.1f,1f,0f };//xyz(direction, not position), w=0 for infinite
  			Gl.glLightfv(Gl.GL_LIGHT0,Gl.GL_AMBIENT,light_ambient);
  			Gl.glLightfv(Gl.GL_LIGHT0,Gl.GL_DIFFUSE,light_diffuse);
  			Gl.glLightfv(Gl.GL_LIGHT0,Gl.GL_SPECULAR,light_specular);
  			Gl.glLightfv(Gl.GL_LIGHT0,Gl.GL_POSITION,light_position);
  			//Materials
  			Gl.glShadeModel(Gl.GL_SMOOTH);
  			//OK to just set these three once.
  			specular_color_normal = new float[] { 1.0f,1.0f,1.0f,1.0f };//1111 for white. RGBA
  			specular_color_cementum = new float[] { 0.1f,0.1f,0.1f,1.0f };//gray
  			shininess = new float[] { 90f };//0 to 128. Size of specular reflection. 128 smallest
  			Gl.glEnable(Gl.GL_LIGHTING);
  			Gl.glEnable(Gl.GL_LIGHT0);
  			//Render Scene starts here----------------------------------------------------------------------------------
  			Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);//Clears the color buffer and depth buffer.
  			//viewing transformation.  gluLookAt is too complex, so not used
  			//default was Z=1, looking towards the origin
  			Gl.glMatrixMode(Gl.GL_MODELVIEW);
  			Gl.glLoadIdentity();//clears the matrix
  			Gl.glMatrixMode(Gl.GL_PROJECTION);//only the projection matrix will be affected.
  			Gl.glLoadIdentity();
  			//double HeightProjection=WidthProjection*this.Height/this.Width;
  			//Gl.glOrtho(-WidthProjection/2,WidthProjection/2,//orthographic projection. L,R
  			//	-HeightProjection/2,HeightProjection/2,//Bot,Top
  			//	-WidthProjection/2,WidthProjection/2);//Near,Far
  			//double widthProj=(double)TcData.SizeOriginalProjection.Width;
  			//double heightProj=widthProj*Height/Width;
  			double heightProj=(double)TcData.SizeOriginalProjection.Height;
  			double widthProj=(double)TcData.SizeOriginalProjection.Width;
  			if(TcData.IsWide) {
  				widthProj=heightProj*Width/Height;
  			}
  			else {//tall
  				heightProj=widthProj*Height/Width;
  			}
  			Gl.glOrtho(-widthProj/2,widthProj/2,//orthographic projection. L,R
  				-heightProj/2,heightProj/2,//Bot,Top
  				-widthProj/2,widthProj/2);//Near,Far
  			Gl.glMatrixMode(Gl.GL_MODELVIEW);
  			//viewport transformation not used. Default is to fill entire control.
  			Gl.glEnable(Gl.GL_BLEND);
  			Gl.glEnable(Gl.GL_LINE_SMOOTH);
  			Gl.glHint(Gl.GL_LINE_SMOOTH_HINT,Gl.GL_DONT_CARE);
  			DrawScene();
  			//jitter code for antialias starts here, but I can't get it to work:
  			//Gl.glFlush();//handled for me in base class
  		}
  
  		private void DrawScene() {
  			ToothChartWrapper wrapper=(ToothChartWrapper)this.Parent;
  			string wrappername=wrapper.Name;
  			bool isRCT8=wrapper.TcData.ListToothGraphics["8"].IsRCT;
  			for(int t=0;t<TcData.ListToothGraphics.Count;t++) {//loop through each tooth
  				if(TcData.ListToothGraphics[t].ToothID=="implant") {//this is not an actual tooth.
  					continue;
  				}
  				DrawFacialView(TcData.ListToothGraphics[t]);
  				DrawOcclusalView(TcData.ListToothGraphics[t]);
  			}
  			// 
  			//bitmapInPictBox=GetBitmapOfOpenGL();
  			//gg=Graphics.FromImage(bitmapInPictBox);
  			DrawWatches();
  			DrawNumbersAndLines();
  			DrawDrawingSegments();
  			//g.DrawImage(bitmapInPictBox,0,0);
  			Gl.glFlush();
  		}
  
  		private void DrawFacialView(ToothGraphic toothGraphic) {
  			Gl.glPushMatrix();//remember position of origin
  			Gl.glTranslatef(TcData.GetTransX(toothGraphic.ToothID),//Move the tooth to the correct position for facial view
  				TcData.GetTransYfacial(toothGraphic.ToothID),
  				0);
  			RotateAndTranslateUser(toothGraphic);
  			if(toothGraphic.Visible
  				|| (toothGraphic.IsCrown && toothGraphic.IsImplant)
  				|| toothGraphic.IsPontic)
  			{
  				DrawTooth(toothGraphic);
  			}
  			Gl.glDisable(Gl.GL_DEPTH_TEST);
  			if(toothGraphic.DrawBigX) {
  				Gl.glDisable(Gl.GL_LIGHTING);
  				Gl.glEnable(Gl.GL_BLEND);
  				//move the bigX 6mm to the Facial so it will paint in front of the tooth
  				Gl.glTranslatef(0,0,6f);
  				Gl.glBlendFunc(Gl.GL_SRC_ALPHA,Gl.GL_ONE_MINUS_SRC_ALPHA);
  				Gl.glLineWidth(2f*TcData.PixelScaleRatio);//thickness of line depends on size of window
  				Gl.glColor3f (
  					(float)toothGraphic.colorX.R/255f,
  					(float)toothGraphic.colorX.G/255f,
  					(float)toothGraphic.colorX.B/255f);
  				if(ToothGraphic.IsMaxillary(toothGraphic.ToothID)){
  					Gl.glBegin(Gl.GL_LINES);
  					Gl.glVertex2f(-2f,12f);
  					Gl.glVertex2f(2f,-6f);
  					Gl.glEnd();
  					Gl.glBegin(Gl.GL_LINES);
  					Gl.glVertex2f(2f,12f);
  					Gl.glVertex2f(-2f,-6f);
  					Gl.glEnd();
  				}
  				else{
  					Gl.glBegin(Gl.GL_LINES);
  					Gl.glVertex2f(-2f,6f);
  					Gl.glVertex2f(2f,-12f);
  					Gl.glEnd();
  					Gl.glBegin(Gl.GL_LINES);
  					Gl.glVertex2f(2f,6f);
  					Gl.glVertex2f(-2f,-12f);
  					Gl.glEnd();
  				}
  			}
  			Gl.glPopMatrix();//reset to origin
  			if(toothGraphic.Visible && toothGraphic.IsRCT) {//draw RCT
  				Gl.glPushMatrix();
  				Gl.glTranslatef(0,0,10f);//move RCT forward 10mm so it will be visible.
  				Gl.glTranslatef(TcData.GetTransX(toothGraphic.ToothID),TcData.GetTransYfacial(toothGraphic.ToothID),0);
  				Gl.glDisable(Gl.GL_LIGHTING);
  				Gl.glEnable(Gl.GL_BLEND);
  				Gl.glColor3f(
  					(float)toothGraphic.colorRCT.R/255f,
  					(float)toothGraphic.colorRCT.G/255f,
  					(float)toothGraphic.colorRCT.B/255f);
  					//.5f);//only 1/2 darkness
  				Gl.glBlendFunc(Gl.GL_SRC_ALPHA,Gl.GL_ONE_MINUS_SRC_ALPHA);
  				Gl.glLineWidth(2.2f*TcData.PixelScaleRatio);
  				Gl.glPointSize(1.8f*TcData.PixelScaleRatio);//point is slightly smaller since no antialiasing
  				RotateAndTranslateUser(toothGraphic);
  				List<LineSimple> lines=toothGraphic.GetRctLines();
  				for(int i=0;i<lines.Count;i++){
  					Gl.glBegin(Gl.GL_LINE_STRIP);
  					for(int j=0;j<lines[i].Vertices.Count;j++){
  						Gl.glVertex3f(lines[i].Vertices[j].X,lines[i].Vertices[j].Y,lines[i].Vertices[j].Z);
  					}
  					Gl.glEnd();
  				}
  				Gl.glPopMatrix();
  				//This section is a necessary workaround for OpenGL.
  				//It draws a point at each intersection to hide the unsightly transitions between line segments.
  				Gl.glPushMatrix();
  				Gl.glTranslatef(0,0,10.5f);//move forward 10.5mm so it will cover the lines
  				Gl.glTranslatef(TcData.GetTransX(toothGraphic.ToothID),TcData.GetTransYfacial(toothGraphic.ToothID),0);
  				RotateAndTranslateUser(toothGraphic);
  				Gl.glDisable(Gl.GL_BLEND);
  				for(int i=0;i<lines.Count;i++){
  					Gl.glBegin(Gl.GL_POINTS);
  					for(int j=0;j<lines[i].Vertices.Count;j++) {
  						//but ignore the first and last.  We are only concerned with where lines meet.
  						if(j==0 || j==lines[i].Vertices.Count-1) {
  							continue;
  						}
  						Gl.glVertex3f(lines[i].Vertices[j].X,lines[i].Vertices[j].Y,lines[i].Vertices[j].Z);
  					}
  					Gl.glEnd();
  				}
  				Gl.glPopMatrix();
  			}
  			ToothGroup groupBU=toothGraphic.GetGroup(ToothGroupType.Buildup);//during debugging, not all teeth have a BU group yet.
  			if(toothGraphic.Visible && groupBU!=null && groupBU.Visible) {//BU or Post
  				Gl.glPushMatrix();
  				Gl.glTranslatef(0,0,13f);//move BU forward 13mm so it will be visible.
  				Gl.glTranslatef(TcData.GetTransX(toothGraphic.ToothID),TcData.GetTransYfacial(toothGraphic.ToothID),0);
  				Gl.glDisable(Gl.GL_LIGHTING);
  				Gl.glDisable(Gl.GL_BLEND);
  				Color colorBU=toothGraphic.GetGroup(ToothGroupType.Buildup).PaintColor;
  				Gl.glColor3f(
  					(float)colorBU.R/255f,
  					(float)colorBU.G/255f,
  					(float)colorBU.B/255f);
  				RotateAndTranslateUser(toothGraphic);
  				Gl.glCallList(displayListOffset+toothGraphic.GetIndexForDisplayList(toothGraphic.GetGroup(ToothGroupType.Buildup)));
  				//Triangle poly=toothGraphic.GetBUpoly();
  				//Gl.glBegin(Gl.GL_POLYGON);
  				//for(int i=0;i<poly.Vertices.Count;i++) {
  				//	Gl.glVertex3f(poly.Vertices[i].X,poly.Vertices[i].Y,poly.Vertices[i].Z);
  				//}
  				//Gl.glEnd();
  				Gl.glPopMatrix();
  			}
  			if(toothGraphic.IsImplant){
  				Gl.glPushMatrix();
  				Gl.glTranslatef(TcData.GetTransX(toothGraphic.ToothID),//Move the tooth to the correct position for facial view
  					TcData.GetTransYfacial(toothGraphic.ToothID),
  					0);
  				RotateAndTranslateUser(toothGraphic);
  				if(ToothGraphic.IsMaxillary(toothGraphic.ToothID)) {
  					//flip the implant upside down
  					Gl.glRotatef(180f,0,0,1f);
  				}
  				Gl.glEnable(Gl.GL_LIGHTING);
  				Gl.glEnable(Gl.GL_BLEND);
  				Gl.glEnable(Gl.GL_DEPTH_TEST);
  				ToothGroup group=(ToothGroup)TcData.ListToothGraphics["implant"].Groups[0];
  				float[] material_color=new float[] {
  					(float)toothGraphic.colorImplant.R/255f,
  					(float)toothGraphic.colorImplant.G/255f,
  					(float)toothGraphic.colorImplant.B/255f,
  					(float)toothGraphic.colorImplant.A/255f
  				};//RGBA
  				Gl.glMaterialfv(Gl.GL_FRONT,Gl.GL_SPECULAR,specular_color_normal);
  				Gl.glMaterialfv(Gl.GL_FRONT,Gl.GL_SHININESS,shininess);
  				Gl.glMaterialfv(Gl.GL_FRONT,Gl.GL_AMBIENT_AND_DIFFUSE,material_color);
  				Gl.glBlendFunc(Gl.GL_ONE,Gl.GL_ZERO);
  				Gl.glHint(Gl.GL_POLYGON_SMOOTH_HINT,Gl.GL_NICEST);
  				for(int i=0;i<group.Faces.Count;i++){//  .GetLength(0);i++) {//loop through each face
  					Gl.glBegin(Gl.GL_POLYGON);
  					for(int j=0;j<group.Faces[i].IndexList.Count;j++){//.Length;j++) {//loop through each vertex
  						//The index for both will always be the same because we enforce a 1:1 relationship.
  						//We show grabbing a float[3], but we could just as easily use the index itself.
  						Gl.glVertex3fv(TcData.ListToothGraphics["implant"].VertexNormals[group.Faces[i].IndexList[j]].Vertex.GetFloatArray());//Vertices[group.Faces[i][j][0]]);
  						Gl.glNormal3fv(TcData.ListToothGraphics["implant"].VertexNormals[group.Faces[i].IndexList[j]].Normal.GetFloatArray()); //.Normals[group.Faces[i][j][1]]);
  					}
  					Gl.glEnd();
  				}
  				Gl.glPopMatrix();
  			}
  		}
  
  		private void DrawOcclusalView(ToothGraphic toothGraphic) {
  			//now the occlusal surface. Notice that it's relative to origin again
  			Gl.glPushMatrix();//remember position of origin
  			Gl.glTranslatef(TcData.GetTransX(toothGraphic.ToothID),TcData.GetTransYocclusal(toothGraphic.ToothID),0);
  			if(ToothGraphic.IsMaxillary(toothGraphic.ToothID)) {
  				Gl.glRotatef(-110f,1f,0,0);//rotate angle about line from origin to x,y,z
  			}
  			else {//mandibular
  				if(ToothGraphic.IsAnterior(toothGraphic.ToothID)) {
  					Gl.glRotatef(110f,1f,0,0);
  				}
  				else {
  					Gl.glRotatef(120f,1f,0,0);
  				}
  			}
  			RotateAndTranslateUser(toothGraphic);
  			if(!Tooth.IsPrimary(toothGraphic.ToothID)//if perm tooth
  				&& Tooth.IsValidDB(Tooth.PermToPri(toothGraphic.ToothID))
  				&& TcData.ListToothGraphics[Tooth.PermToPri(toothGraphic.ToothID)].Visible)//and the primary tooth is visible
  			{
  				//do not paint
  			}
  			else if(toothGraphic.Visible//might not be visible if an implant
  				|| (toothGraphic.IsCrown && toothGraphic.IsImplant))//a crown on an implant will paint
  				//pontics won't paint, because tooth is invisible
  			{
  				DrawTooth(toothGraphic);
  			}
  			Gl.glPopMatrix();//reset to origin
  			if(toothGraphic.Visible && toothGraphic.IsSealant){//draw sealant
  				Gl.glPushMatrix();
  				Gl.glTranslatef(0,0,6f);//move forward 6mm so it will be visible.
  				Gl.glTranslatef(TcData.GetTransX(toothGraphic.ToothID),TcData.GetTransYocclusal(toothGraphic.ToothID),0);
  				if(ToothGraphic.IsMaxillary(toothGraphic.ToothID)) {
  					Gl.glRotatef(-110f,1f,0,0);//rotate angle about line from origin to x,y,z
  				}
  				else {//mandibular
  					if(ToothGraphic.IsAnterior(toothGraphic.ToothID)) {
  						Gl.glRotatef(110f,1f,0,0);
  					}
  					else {
  						Gl.glRotatef(120f,1f,0,0);
  					}
  				}
  				Gl.glDisable(Gl.GL_LIGHTING);
  				Gl.glEnable(Gl.GL_BLEND);
  				Gl.glColor3f(
  					(float)toothGraphic.colorSealant.R/255f,
  					(float)toothGraphic.colorSealant.G/255f,
  					(float)toothGraphic.colorSealant.B/255f);
  				//.5f);//only 1/2 darkness
  				Gl.glBlendFunc(Gl.GL_SRC_ALPHA,Gl.GL_ONE_MINUS_SRC_ALPHA);
  				Gl.glLineWidth(2f*TcData.PixelScaleRatio);
  				Gl.glPointSize(1.7f*TcData.PixelScaleRatio);//point is slightly smaller since no antialiasing
  				RotateAndTranslateUser(toothGraphic);
  				LineSimple line=toothGraphic.GetSealantLine();
  				Gl.glBegin(Gl.GL_LINE_STRIP);
  				for(int j=0;j<line.Vertices.Count;j++) {//loop through each vertex
  					Gl.glVertex3f(line.Vertices[j].X,line.Vertices[j].Y,line.Vertices[j].Z);
  				}
  				Gl.glEnd();
  				//The next 30 or so lines are all a stupid OpenGL workaround to hide the line intersections with big dots.
  				Gl.glPopMatrix();
  				//now, draw a point at each intersection to hide the unsightly transitions
  				Gl.glPushMatrix();
  				//move foward so it will cover the lines
  				Gl.glTranslatef(0,0,6.5f);
  				Gl.glTranslatef(TcData.GetTransX(toothGraphic.ToothID),TcData.GetTransYocclusal(toothGraphic.ToothID),0);
  				if(ToothGraphic.IsMaxillary(toothGraphic.ToothID)) {
  					Gl.glRotatef(-110f,1f,0,0);//rotate angle about line from origin to x,y,z
  				}
  				else {//mandibular
  					if(ToothGraphic.IsAnterior(toothGraphic.ToothID)) {
  						Gl.glRotatef(110f,1f,0,0);
  					}
  					else {
  						Gl.glRotatef(120f,1f,0,0);
  					}
  				}
  				RotateAndTranslateUser(toothGraphic);
  				Gl.glDisable(Gl.GL_BLEND);
  				Gl.glBegin(Gl.GL_POINTS);
  				for(int j=0;j<line.Vertices.Count;j++) {//loop through each vertex
  					//but ignore the first and last.  We are only concerned with where lines meet.
  					if(j==0 || j==line.Vertices.Count-1) {
  						continue;
  					}
  					Gl.glVertex3f(line.Vertices[j].X,line.Vertices[j].Y,line.Vertices[j].Z);
  				}
  				Gl.glEnd();
  				Gl.glPopMatrix();
  			}
  		}
  
  		private void DrawWatches(){
  			Gl.glDisable(Gl.GL_LIGHTING);
  			Gl.glDisable(Gl.GL_BLEND);
  			Gl.glDisable(Gl.GL_DEPTH_TEST);
  			Hashtable watchTeeth=new Hashtable(TcData.ListToothGraphics.Count);
  			for(int t=0;t<TcData.ListToothGraphics.Count;t++) {//loop through each adult tooth
  			  ToothGraphic toothGraphic=TcData.ListToothGraphics[t];
  				//If a tooth is marked to be watched then it is always visible, even if the tooth is missing/hidden.
  			  if(toothGraphic.ToothID=="implant" || !toothGraphic.Watch || Tooth.IsPrimary(toothGraphic.ToothID)) {
  			    continue;
  			  }
  			  watchTeeth[toothGraphic.ToothID]=toothGraphic;
  			}
  			for(int t=0;t<TcData.ListToothGraphics.Count;t++) {//loop through each primary tooth
  			  ToothGraphic toothGraphic=TcData.ListToothGraphics[t];
  				//If a tooth is marked to be watched then it is always visible, even if the tooth is missing/hidden.
  			  if(toothGraphic.ToothID=="implant"|| !toothGraphic.Watch || !Tooth.IsPrimary(toothGraphic.ToothID) || !toothGraphic.Visible) {
  			    continue;
  			  }
  			  watchTeeth[Tooth.PriToPerm(toothGraphic.ToothID)]=toothGraphic;
  			}
  			foreach(DictionaryEntry toothGraphic in watchTeeth){
  			  RenderToothWatch((ToothGraphic)toothGraphic.Value);
  			}
  		}
  
  		private void RenderToothWatch(ToothGraphic toothGraphic){
  			Gl.glPushMatrix();
  			if(ToothGraphic.IsRight(toothGraphic.ToothID)) {
  				Gl.glTranslatef(TcData.GetTransX(toothGraphic.ToothID)+toothGraphic.ShiftM,0,0);
  			} 
  			else {
  				Gl.glTranslatef(TcData.GetTransX(toothGraphic.ToothID)-toothGraphic.ShiftM,0,0);
  			}
  			float toMm=1f/TcData.ScaleMmToPix;
  			LineSimple line=toothGraphic.GetWatchLine();
  			Gl.glLineWidth(3f*TcData.PixelScaleRatio);
  			Gl.glColor3f(1.0f,1.0f,1.0f);//White
  			Gl.glBegin(Gl.GL_LINE_STRIP);
  			for(int j=0;j<line.Vertices.Count;j++) {//loop through each vertex and render the white W lines.
  				if(ToothGraphic.IsMaxillary(toothGraphic.ToothID)){
  					Gl.glVertex3f(line.Vertices[j].X+TcData.PixelScaleRatio*(-6f*toMm),line.Vertices[j].Y+TcData.PixelScaleRatio*(150f*toMm),line.Vertices[j].Z-7f);
  				}
  				else{
  					Gl.glVertex3f(line.Vertices[j].X+TcData.PixelScaleRatio*(-6f*toMm),line.Vertices[j].Y+TcData.PixelScaleRatio*(-140f*toMm),line.Vertices[j].Z-7f);
  				}
  			}
  			Gl.glEnd();
  			Gl.glLineWidth(0.6f*TcData.PixelScaleRatio);
  			Gl.glColor3f(
  				(float)toothGraphic.colorWatch.R/255f,
  				(float)toothGraphic.colorWatch.G/255f,
  				(float)toothGraphic.colorWatch.B/255f);
  			Gl.glBegin(Gl.GL_LINE_STRIP);
  			for(int j=0;j<line.Vertices.Count;j++) {//loop through each vertex and render the colored W lines.
  				if(ToothGraphic.IsMaxillary(toothGraphic.ToothID)){
  					Gl.glVertex3f(line.Vertices[j].X+TcData.PixelScaleRatio*(-6f*toMm),line.Vertices[j].Y+TcData.PixelScaleRatio*(150f*toMm),line.Vertices[j].Z-6f);
  				}
  				else{
  					Gl.glVertex3f(line.Vertices[j].X+TcData.PixelScaleRatio*(-6f*toMm),line.Vertices[j].Y+TcData.PixelScaleRatio*(-140f*toMm),line.Vertices[j].Z-6f);
  				}
  			}
  			Gl.glEnd();
  			Gl.glDisable(Gl.GL_BLEND);
  			Gl.glPointSize(2f*TcData.PixelScaleRatio);
  			Gl.glColor3f(1.0f,1.0f,1.0f);//White
  			Gl.glBegin(Gl.GL_POINTS);
  			for(int j=0;j<line.Vertices.Count;j++) {//loop through each vertex and render the colored W points.
  			  if(ToothGraphic.IsMaxillary(toothGraphic.ToothID)){
  			    Gl.glVertex3f(line.Vertices[j].X+TcData.PixelScaleRatio*(-6f*toMm),line.Vertices[j].Y+TcData.PixelScaleRatio*(150f*toMm),line.Vertices[j].Z+7f);
  			  }
  			  else{
  			    Gl.glVertex3f(line.Vertices[j].X+TcData.PixelScaleRatio*(-6f*toMm),line.Vertices[j].Y+TcData.PixelScaleRatio*(-140f*toMm),line.Vertices[j].Z+7f);
  			  }
  			}
  			Gl.glEnd();
  			Gl.glPointSize(0.4f*TcData.PixelScaleRatio);//point is slightly smaller since no antialiasing
  			Gl.glColor3f(
  				(float)toothGraphic.colorWatch.R/255f,
  				(float)toothGraphic.colorWatch.G/255f,
  				(float)toothGraphic.colorWatch.B/255f);
  			Gl.glBegin(Gl.GL_POINTS);
  			for(int j=0;j<line.Vertices.Count;j++) {//loop through each vertex and render the colored W points.
  			  if(ToothGraphic.IsMaxillary(toothGraphic.ToothID)){
  			    Gl.glVertex3f(line.Vertices[j].X+TcData.PixelScaleRatio*(-6f*toMm),line.Vertices[j].Y+TcData.PixelScaleRatio*(150f*toMm),line.Vertices[j].Z+6f);
  			  }
  			  else{
  			    Gl.glVertex3f(line.Vertices[j].X+TcData.PixelScaleRatio*(-6f*toMm),line.Vertices[j].Y+TcData.PixelScaleRatio*(-140f*toMm),line.Vertices[j].Z+6f);
  			  }
  			}
  			Gl.glEnd();
  			Gl.glPopMatrix();
  		}
  
  		private void DrawNumbersAndLines() {
  			Gl.glPushMatrix();
  			Gl.glDisable(Gl.GL_LIGHTING);
  			Gl.glDisable(Gl.GL_BLEND);
  			Gl.glDisable(Gl.GL_DEPTH_TEST);
  			Gl.glColor3f(
  				(float)TcData.ColorText.R/255f,
  				(float)TcData.ColorText.G/255f,
  				(float)TcData.ColorText.B/255f);
  			//this still doesn't seem to work quite right, but it's tolerable
  			Gl.glLineWidth(1f*TcData.PixelScaleRatio);
  			Gl.glBegin(Gl.GL_LINE_STRIP);
  				//Gl.glVertex3f(-(float)WidthProjection/2f,0,0);
  				//Gl.glVertex3f((float)WidthProjection/2f,0,0);
  				Gl.glVertex3f(-TcData.SizeOriginalProjection.Width/2f,0,0);
  				Gl.glVertex3f(TcData.SizeOriginalProjection.Width/2f,0,0);
  			Gl.glEnd();
  			string tooth_id;
  			for(int i=1;i<=52;i++){
  				tooth_id=Tooth.FromOrdinal(i);
  				if(TcData.SelectedTeeth.Contains(tooth_id)) {
  					DrawNumber(tooth_id,true,true);
  				}
  				else {
  					DrawNumber(tooth_id,false,true);
  				}
  			}
  			Gl.glPopMatrix();
  		}
  
  		private void DrawDrawingSegments(){
  			/*string[] pointStr;
  			List<Point> points;
  			Point point;
  			string[] xy;
  			Pen pen;
  			for(int s=0;s<TcData.DrawingSegmentList.Count;s++) {
  				pen=new Pen(TcData.DrawingSegmentList[s].ColorDraw,2f);
  				pointStr=TcData.DrawingSegmentList[s].DrawingSegment.Split(';');
  				points=new List<Point>();
  				for(int p=0;p<pointStr.Length;p++) {
  					xy=pointStr[p].Split(',');
  					if(xy.Length==2) {
  						point=new Point(int.Parse(xy[0]),int.Parse(xy[1]));
  						points.Add(point);
  					}
  				}
  
gt: 				for(int i=1;i<points.Count;i++) {