using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using System.Collections;

using Spherical.HTM;
using Spherical;

namespace hcalc
{
	// The canvas draw and renders HTM globes and trixels over them
	// Also knows how to draw convexes, but doesn't yet fill them
	// 
	public class Htmcanvas
	{
		// draw_hid() only manipulates hid
		// draw_wTriangle draws world coordinates

		// un-XX this when you need it, now it is a reminder
		// for what I want to do.
		public enum XXendertype {
			highlight,
			grid,
			iTrace,		// hids from the trace file
			cTrace,		// halfspaces from the trace file
			mTrace,		// mouse following
		}
		internal class Step
		{
			public Int64 hid;
			public string reason;
			public Step(Int64 h, string r) {
				hid = h;
				reason = r;
			}
		}
        internal struct TracedHid {
            public Int64 hid;
            public bool full;
            public TracedHid(Int64 h, bool f) {
                hid = h;
                full = f;
            }
        }
        public double twistAngle = 0.0;
        private double _twistAngle = 0.0;
        public long tick = 0; // 99999900;
		// Customaizable colors
		// eraseto
		// pickedhid (yellow)
		// 
		//
		// erase              : Clears to DarkSlateGray
		// draw_arc           : _customPen
		// draw_halfspace     : _customPen 
		// trackMouse         : draw_hid :
		// globalGrid         : draw_hid : 
		// renderPickedHID    : draw_hid : (Yellow)
		// draw_hid           : draw_WorldTriangle :
		// draw_WorldTriangle : _pen
		// draw_worldSq       : _pen
		// draw_worldDot      : _pen
		// 

		private bool _showgrid = false;
		private bool _showconvexes = false;
		private bool _showtrixels = false;
		private bool _showaxes = true;
		private bool _showduper = false;
        private bool _showannotate = false;
        private bool _curvetrixels = false;
		protected System.Drawing.Font font = null;

		private int oldu, oldv;
		private String[] _loNames = { "S0", "S00", "S000", "S0000", "S00000", "S000000" };
		private String[] _hiNames = { "N3", "N33", "N333", "N3333", "N33333", "N333333" };
		//
		// Projection parameters
		//
		//
		// the trixel3d oject will rotate itself as it wants
		// the canvas cares about the camera only and the image
		// plane
		// Animation parameters

		private Camera3D _camera;
		private Cartesian w0, w1, w2;
		// ///////////////////////////////////////////////////////////////////
		// Stuff that gets set from the windows form
		//
		private Int64 _form_hid;
		private Int64 _mouse_hid;

		public Spherical.Region sreg;
        public Spherical.Convex scon;

		private Halfspace _halfspace;
		private ArrayList _tracedhalfspaces;
		private List<TracedHid> _tracedHIDs;
		private ArrayList _algSteps;
		private int _algStepNumber;
		private bool _algEnabled;

		private int _grid_level = 3;
		private int _zoom_level = 0;
		private double _zoom = 1.0;

		// //////////////////////////////////////////////////////////////////
		// Local stuff
		//
		private Point _locationTL;
		private Point _locationBR;
		private StringBuilder _captionTL;
		private StringBuilder _captionBR;

		
		private Pen _pen;
		private Pen _arcPen; // used in drawworldtriangle only
		private Pen _customPen; // used in drawing triangles traced 
		// with interactive mouse following

		private int _ringsize;
		private int _narcs;
		private bool _dragging = false;
		//
		//

		public void purge() {
			_arcPen.Dispose();
			_customPen.Dispose();
			font.Dispose();
		}
		private void init_component() {
			font = new Font("Arial", 10);
			_locationTL = new Point(10, 10);
			_locationBR = new Point();	// Must fill this later
			
			_camera = new Camera3D();
			_arcPen = new Pen(System.Drawing.Color.Yellow, 4.0f);
			_customPen = new Pen(System.Drawing.Color.Orange, 2.0f);
			_pen = System.Drawing.Pens.Black;
			w0 = new Cartesian();
			w1 = new Cartesian();
			w2 = new Cartesian();
			_captionTL = new StringBuilder();
			_captionBR = new StringBuilder();
			_narcs = 16; // This should be resolution dependent;
			_ringsize = 720;
			_mouse_hid = -1;
			_halfspace = new Halfspace();
			_tracedhalfspaces = new ArrayList();
            sreg = new Spherical.Region();
            scon = new Spherical.Convex();
            _tracedHIDs = new List<TracedHid>();
			_algSteps = new ArrayList();
			_algStepNumber = 0;
			_algEnabled = false;
			// sreg = null;

		}
		public Htmcanvas() {

			init_component();
		}
		[CLSCompliant(false)]
		protected void draw_worldSq(Graphics g, Cartesian v0) {
			Point[] points = new Point[3];

			_camera.rotate(v0, ref w0);					// v is rotated into camera's world, w


			//
			// Compute z component sign of surface normal
			// almost like corss product, except no
			// need to compute the x and y components
			// 
			if (w0.Z >= 0.0) {
				_camera.dw2vp(w0, ref points[0]);

				int x = points[0].X;
				int y = points[0].Y;

				g.DrawRectangle(_pen, x - 4, y - 4, 8, 8);
			}
		}
        

		protected void draw_worldDot(Graphics g, Cartesian v0) {
			Point[] points = new Point[3];			

			_camera.rotate(v0, ref w0);					// v is rotated into camera's world, w


			//
			// Compute z component sign of surface normal
			// almost like corss product, except no
			// need to compute the x and y components
			// 
			if (w0.Z >= 0.0) {
				_camera.dw2vp(w0, ref points[0]);

				int x = points[0].X;
				int y = points[0].Y;
				
				g.DrawArc(_pen, x - 2, y - 2, 4, 4, 0, 360);
			}
		}
		protected void draw_worldDot(Graphics g, Cartesian v0, Cartesian v1, Cartesian v2) {
			Point[] points = new Point[3];
	//		Cartesian axis = new Cartesian();	// incremental rotation around this axis
			double[,] mat = new double[3, 3];	// ... with this ad-hoc matrix
			double[] ww = new double[3];		// ... using this vector to arc the triangle side

			_camera.rotate(v0, ref w0);					// v is rotated into camera's world, w
			_camera.rotate(v1, ref w1);
			_camera.rotate(v2, ref w2);					// camera is in its Z axis... parallel projection now

			//
			// Compute z component sign of surface normal
			// almost like corss product, except no
			// need to compute the x and y components
			// 
			double ax, ay, bx, by;
			double z;
			ax = w1.X - w0.X;
			ay = w1.Y - w0.Y;

			bx = w2.X - w1.X;
			by = w2.Y - w1.Y;

			z = ax * by - ay * bx;

			if (z >= 0.0) {
				_camera.dw2vp(w0, ref points[0]);
				_camera.dw2vp(w1, ref points[1]);
				_camera.dw2vp(w2, ref points[2]);
				

				int x, y;
				x = points[0].X + points[1].X + points[2].X;
				y = points[0].Y + points[1].Y + points[2].Y;
				x /= 3;
				y /= 3;
				g.DrawArc(_pen, x-2, y-2, 4, 4, 0, 360);
			}
		}
        protected void draw_WorldSegment(Graphics g, Cartesian v0, Cartesian v1, bool arced) {
            Point p0 = new Point();
            Point p1 = new Point();
            double z;
            //Cartesian vp0 = new Cartesian();
            //Cartesian vp1 = new Cartesian();
            // Cartesian axis = new Cartesian();	// incremental rotation around this axis
            double[,] mat = new double[3, 3];	// ... with this ad-hoc matrix

            _camera.rotate(v0, ref w0);					// v is rotated into camera's world, w
            _camera.rotate(v1, ref w1);
            // camera is in its Z axis... parallel projection now
            //
            // Arced ones are drawn with Yellow or Pink, rendertype ignored
            // using highlightpen

            z = (w0.Z + w1.Z) / 2.0;
                
                

                if(z >= 0.0) {
                    _camera.dw2vp(w0, ref p0);
                    _camera.dw2vp(w1, ref p1);
                    g.DrawLine(System.Drawing.Pens.Orange, p0, p1);

                    //int x, y, w, h;
                    //x = points[0].X + points[1].X + points[2].X;
                    //y = points[0].Y + points[1].Y + points[2].Y;
                    //x /= 3;
                    //y /= 3;
                    // dot in center g.DrawArc(_pen, x-2, y-2, 4, 4, 0, 360); 

                }

        }
		//
		// All triangles should be drawn here
		// arced triangles are drawn with arcPen
        // 
        protected void XXdraw_worldTriangle(
            Graphics g, Cartesian v0, Cartesian v1, Cartesian v2,
            bool arced) {
            draw_worldTriangle(g, v0, v1, v2, arced, 
                System.Drawing.Color.Yellow, 3.0f);
        }
		protected void draw_worldTriangle(
            Graphics g, Cartesian v0, Cartesian v1, Cartesian v2,
			bool arced,
            Color color, float width) {
			Point[] points = new Point[3];
			Cartesian axis = new Cartesian();	// incremental rotation around this axis
			double[,] mat = new double[3, 3];	// ... with this ad-hoc matrix
			Cartesian ww = new Cartesian();		// ... using this vector to arc the triangle side
			double angle;
			double delta_angle;

			_camera.rotate(v0, ref w0);					// v is rotated into camera's world, w
			_camera.rotate(v1, ref w1);
			_camera.rotate(v2, ref w2);					// camera is in its Z axis... parallel projection now
			//
			// Arced ones are drawn with Yellow or Pink, rendertype ignored
			// using highlightpen
			#region ARC
			if (arced && (w0.Z > 0.0 || w1.Z > 0.0 || w2.Z > 0.0)) {
				Point[] arcPoints = new Point[_narcs];
				if (w0.Z > 0.0 && w1.Z > 0.0 && w2.Z > 0.0) {
					_arcPen.Color = color;
					_arcPen.Width = width;
				} else {
					_arcPen.Width = 1.0f;
					_arcPen.Color = System.Drawing.Color.Pink;
				}
				// Rotate point from w0 into w1, and so on
				// need: get axis of rotation given two points
				// need: rotate a point around axis by theta
				//
				Camera3D.getAxisandAngle(w0, w1, ref axis, out angle);
				delta_angle = angle / (_narcs - 1);
				for (int i = 0; i < _narcs; i++) {
					Camera3D.getRotationMatrix(mat, axis, i * delta_angle);
					Camera3D.rotate(w0, mat, ref ww);
					_camera.dw2vp(ww, ref arcPoints[i]);
				}
				g.DrawLines(_arcPen, arcPoints);
				//
				// Repeat two mnore times
				//
				Camera3D.getAxisandAngle(w1, w2, ref axis, out angle);
				delta_angle = angle / (_narcs - 1);
				for (int i = 0; i < _narcs; i++) {
					Camera3D.getRotationMatrix(mat, axis, i * delta_angle);
					Camera3D.rotate(w1, mat, ref ww);
					_camera.dw2vp(ww, ref arcPoints[i]);
				}
				g.DrawLines(_arcPen, arcPoints);
				//
				// This is the third and last time
				//
				Camera3D.getAxisandAngle(w2, w0, ref axis, out angle);
				delta_angle = angle / (_narcs - 1);
				for (int i = 0; i < _narcs; i++) {
					Camera3D.getRotationMatrix(mat, axis, i * delta_angle);
					Camera3D.rotate(w2, mat, ref ww);
					_camera.dw2vp(ww, ref arcPoints[i]);
				}
				g.DrawLines(_arcPen, arcPoints);
			} 
#endregion 
			else {
                _arcPen.Color = color;
                _arcPen.Width = width;

				// commented out code may be a bit faster if the resolution is high
				// and you have a gazillion triangles to render
				// but the proper way is  the surface normal bit...
				//int visible_vertices;
				//visible_vertices = 0;
				//visible_vertices += (w0[2] > 0.0) ? 1 : 0;
				//visible_vertices += (w1[2] > 0.0) ? 1 : 0;
				//visible_vertices += (w2[2] > 0.0) ? 1 : 0;
				//if (visible_vertices >= 2) { // cheap hidden line stuff, show if above Z plane
				//
				// Compute z component sign of surface normal
				// almost like corss product, except no
				// need to compute the x and y components
				// 
				double ax, ay, bx, by;
				double z;
				ax = w1.X - w0.X;
				ay = w1.Y - w0.Y;

				bx = w2.X - w1.X;
				by = w2.Y - w1.Y;

				z = ax * by - ay * bx;

				if (z >= 0.0) {
					_camera.dw2vp(w0, ref points[0]);
					_camera.dw2vp(w1, ref points[1]);
					_camera.dw2vp(w2, ref points[2]);
					g.DrawPolygon(_arcPen, points);

					//int x, y, w, h;
					//x = points[0].X + points[1].X + points[2].X;
					//y = points[0].Y + points[1].Y + points[2].Y;
					//x /= 3;
					//y /= 3;
					// dot in center g.DrawArc(_pen, x-2, y-2, 4, 4, 0, 360); 
				}
			}

		}
		public void setXYZ(double x, double y, double z) {
			_halfspace.Vector.Set(x, y, z, false);
		}
		public void setHalfspaceD(double d) {
			_halfspace.Cos0 = d;
		}
		public void sethid(Int64 hid) {
			_form_hid = hid;
		}
		public int getZoomLevel() {
			return _zoom_level;
		}
		public void setGridlevel(int level) {
			_grid_level = level;
		}
		public void setZoom(int level) {
			_zoom_level = level;
			_zoom = 1.0;
			for (int i = 0; i < level; i++) {
				_zoom /= 2;
			}
			_camera.setZoom(_zoom);
		}
        internal void view_rotate(double dangle, bool absolute) {
            double[] P = new double[3];
            double[] Q = new double[3];
            double[,] smallrot = new double[3, 3];
            if(Math.Abs(dangle) < 0.001)
                return;

            P[0] = 1.0;
            P[1] = 0.0;
            P[2] = 0.0;
            Q[0] = Math.Cos(dangle);
            Q[1] = Math.Sign(dangle);
            Q[2] = 0.0;
            _camera.rotinto(smallrot, P, Q);
            _camera.M_pre_multiply(smallrot);
        }
        internal void polar_rotate(double dangle) {
            double[] P = new double[3];
            double[] Q = new double[3];
            double[,] smallrot = new double[3, 3];
            P[0] = 1.0;
            P[1] = 0.0;
            P[2] = 0.0;
            Q[0] = Math.Cos(dangle);
            Q[1] = Math.Sign(dangle);
            Q[2] = 0.0;
            _camera.rotinto(smallrot, P, Q);
            _camera.M_post_multiply(smallrot);
        }
		internal Int64 mousemove(int u, int v, MouseButtons mouseButtons, int level) {
			this._dragging = (mouseButtons == MouseButtons.Left);
			double[] P = new double[3];
			double[] Q = new double[3];
			bool notoff;
			_mouse_hid = mouse2HID(u, v, level);
			// this._captionTL = new StringBuilder();
			if (_mouse_hid >= 8) {
				this._captionTL.Length = 0;
				this._captionTL.AppendFormat("{0}", Trixel.ToString(_mouse_hid));
			} else {
				this._captionTL.Length = 0;
				this._captionTL.AppendFormat("Off");
			}
//			this._captionBR.Length = 0;
//			this._captionBR.AppendFormat("{0},{1}",u,v);
			//
			// Is mouse off the "sphere"?
			// If mouse HID is < 8 then it is off, else no
			notoff = (_mouse_hid >= 8);
			if (notoff) {
				if (_dragging && notoff) {
					if (Math.Abs(u - oldu) + Math.Abs(v - oldv) > 1) {
						double[,] smallrot = new double[3, 3];
						//
						// New rotation is adding this little rotation
						// to the existing rotation.
						// That is, post multiply the old matrix
						// with the new one here:
						vp2cartesian(oldu, oldv, P);
						vp2cartesian(u, v, Q);

						_camera.unrotate(P);
						_camera.unrotate(Q); // P and Q are in actual world coords
						// Find a small rotatoin marix that takes P into Q
						// and add that to the global matrix (post multiply)
						_camera.rotinto(smallrot, P, Q);
						_camera.M_post_multiply(smallrot);
					}

				}
				oldu = u;
				oldv = v;
			}
			return _mouse_hid;
		}

		public void vp2cartesian(int u, int v, double[] P) {
			vp2cartesian(u, v, out P[0], out P[1], out P[2]);
		}
		public void vp2cartesian(int u, int v, out double x, out double y, out double z) {
			_camera.vp2dw(u, v, out x, out y);
			z = (1.0 - (x * x + y * y));
			z = Math.Sqrt(z);
		}
		
		public Int64 mouse2HID(int u, int v, int level) {
			Int64 hid;
			double x, y, z;
			double[] pin = new double[3];

			vp2cartesian(u, v, out x, out y, out z);
			pin[0] = x;
			pin[1] = y;
			pin[2] = z;
			if (z >= 0.0) {
				_camera.unrotate(out x, out y, out z, pin);
				hid = Trixel.CartesianToHid(x, y, z, level);
			} else {
				hid = -1;
			}
			// On update, this will be drawn			
			return hid;
		}
		public void footnote(string format, long a) {
			this._captionBR.Length = 0; // = new StringBuilder();
			this._captionBR.AppendFormat(format, a);
		}
		public void footnote(string format, int a, int b) {

			this._captionBR.Length = 0; // = new StringBuilder();
			this._captionBR.AppendFormat(format, a, b);
		}
		public void caption2(string format, int a, int b) {
			_captionTL.Length = 0; // = new StringBuilder();
			this._captionTL.AppendFormat(format, a, b);
		}
		//
		// Trace algorithm:
		// enable/disable trace
		// rewind
		// step to next
		//
		public void alg_enable(bool flag) {
			_algEnabled = flag;
		}
		public void alg_rewind() {
			_algStepNumber = 0;
		}
		public string alg_getText() {
			try {
				Step step = (Step)_algSteps[_algStepNumber];
				return step.reason;
			} catch {
			}
			return "No steps to display";
		}
		public void alg_step(int nrsteps) {
			_algStepNumber += nrsteps;
			if (_algStepNumber < 0) {
				_algStepNumber = 0;
			} else  if (_algStepNumber == _algSteps.Count - 1 ){
				_algStepNumber = _algSteps.Count - 1;
			}
		}
		public void alg_setDisplay() {
			try {
				Step step = (Step) _algSteps[_algStepNumber];
				centerHid(step.hid);
			} catch {
			}
		}
		protected void centerHid(Int64 hid) {
			Cartesian v0, v1, v2;
			if (hid >= 8L) {
				Trixel.ToTriangle(hid, out v0, out v1, out v2); 
				double ra, dec;
//				char[] name = new char[Trixel.eMaxNameSize];
//				Trixel.ToName(name, hid);
//				Trixel.NameToTriangle(name, out v0, out v1, out v2);
				List<Cartesian> triangle = new List<Cartesian>();
				triangle.Add(v0);
				triangle.Add(v1);
				triangle.Add(v2);
				Cartesian center = Cartesian.CenterOfMass(triangle, true);
				Cartesian.Xyz2Radec(center.X, center.Y, center.Z, out ra, out dec);
				_camera.set_camera(ra, dec);
			}
		}
		//
		// Centerhid button calls this:
		//
		public void center_formhid() {
			centerHid(this._form_hid);
			//double[] v0 = new double[3];
			//double[] v1 = new double[3];
			//double[] v2 = new double[3];
			//if (this._form_hid >= 8L) {
			//    double ra, dec;
			//    double x, y, z;
			//    double norm;
			//    char[] name = new char[HtmTrixel.eMaxNameSize];
			//    _trixel.hid2name(name, this._form_hid);
			//    _trixel.name2Triangle(name, v0, v1, v2);
			//    x = v0[0] + v1[0] + v2[0];
			//    y = v0[1] + v1[1] + v2[1];
			//    z = v0[2] + v1[2] + v2[2];
			//    norm = x * x + y * y + z * z;
			//    norm = Math.Sqrt(norm);
			//    x /= norm;
			//    y /= norm;
			//    z /= norm;

			//    SpatialVector.Xyz2radec(x, y, z, out ra, out dec);
			//    _t3.set_camera(ra, dec);
			//}
		}
		//
		// contents is called when repaint is warranted
		//

		protected void erase(Graphics g) {
			g.Clear(System.Drawing.Color.DarkSlateGray);
		}
		protected void globalGrid(Graphics g, int n) {
			
			if (n < 0) n = 0;
			if (n > 5) n = 5;
			Int64 hidlo = Trixel.NameToHid(this._loNames[n]);
			Int64 hidhi = Trixel.NameToHid(this._hiNames[n]);
			for (Int64 hid = hidlo; hid <= hidhi; hid++) {
				draw_hid(g, hid, false, System.Drawing.Color.LightSlateGray, 1.0f);
			}

		}
		protected void trackMouse(Graphics g) {
			if (_mouse_hid >= 8) {
				draw_hid(g, this._mouse_hid, false, Color.Red, 1.0f);
			}
		}
		protected void renderPickedHID(Graphics g) {
			draw_hid(g, this._form_hid, true, System.Drawing.Color.Yellow, 2.0f);
			// draw_hid(g, 8, true, Rendertype.highlight);
		}

		//protected void XXdraw_halfspace(Graphics g, Halfspace h, Cartesian p1, Cartesian p2){
		//    int n = _ringsize;
		//    double theta = 0.0;
		//    double dtheta = 2.0 * Math.PI / (n - 1);
		//    double[,] mat = new double[3, 3];
		//    Cartesian startpoint = new Cartesian();
		//    Cartesian endpoint = new Cartesian();
		//    Point[] hsPoints = new Point[n];
		//    // Pen pen = new Pen(System.Drawing.Brushes.Aqua, 2.0f);
		//    theta = 0.0;
		//    //
		//    // Pick a point perpendicular to halfspace axis
		//    // pivot on the axis of component with smallest absolute value
		//    // choose one of (x, y, 0) or (x, 0, z) or (0, y, z)

		//    double x, y, z, norm;
		//    x = Math.Abs(h.X);
		//    y = Math.Abs(h.Y);
		//    z = Math.Abs(h.Z); // (x,y,z) is normalized
		//    int dim;
		//    if (x <= y && x <= z) {
		//        dim = 0;
		//    } else if (y <= x && y <= z) {
		//        dim = 1;
		//    } else {
		//        dim = 2;
		//    }
		//    switch (dim) {
		//        case 0:
		//            x = 0.0;
		//            y = -h.Z;
		//            z = h.Y;
		//            break;
		//        case 1:
		//            x = -h.Z;
		//            y = 0.0;
		//            z = h.X;
		//            break;
		//        default:
		//            x = -h.Y;
		//            y = h.X;
		//            z = 0.0;
		//            break;
		//    }
		//    //
		//    // (x,y,z) is perpendicular to h.sv, but not yet normalized
		//    // proper radius is sqrt(1 - d^2)
		//    //
		//    double rad = Math.Sqrt(1.0 - h.cos0 * h.cos0);
		//    norm = 1.0 / Math.Sqrt(x * x + y * y + z * z);
		//    norm *= rad;
		//    x *= norm;
		//    y *= norm;
		//    z *= norm;
		//    x += h.cos0 * h.X;
		//    y += h.cos0 * h.Y;
		//    z += h.cos0 * h.Z;
		//    endpoint.X = x;
		//    endpoint.Y = y;
		//    endpoint.Z = z;
		//    _t3.rotate(endpoint, startpoint);
		//    Cartesian axis = new Cartesian(h);
		//    _t3.rotate(h, axis);
		//    theta = 0.0;
		//    //
		//    // This loop fills a buffer with the entire 
		//    // outline of the halfspace, whether or not
		//    // it is visible, it will be drawn
		//    //
		//    //for (int i = 0; i < n; i++) {
		//    //    theta += dtheta;
		//    //    _t3.getRotationMatrix(mat, axis, theta);
		//    //    Camera3D.rotate(startpoint, mat, endpoint);
		//    //    _t3.dw2vp(endpoint, ref hsPoints[i]);
		//    //}
		//    //g.DrawLines(pen, hsPoints);

		//    //
		//    // This variant draws a segment, if at least one point is visible
		//    //
		//    int ox = -1, oy = -1, nx = -1, ny = -1;
		//    Point nPoint = new Point();

		//    for (int i = 0; i < n; i++) {
		//        theta += dtheta;
		//        Camera3D.getRotationMatrix(mat, axis, theta);
		//        Camera3D.rotate(startpoint, mat, endpoint);
		//        _t3.dw2vp(endpoint, ref nPoint);
		//        nx = nPoint.X;
		//        ny = nPoint.Y;
		//        if (i > 0 && endpoint.Z > 0.0) { // Cheap hidden line 
		//            g.DrawLine(_customPen, ox, oy, nx, ny);
		//        }
		//        ox = nx;
		//        oy = ny;
		//    }
		//    // Make two big blobs at p1, p2;
		//    draw_worldDot(g, p1);
		//    draw_worldDot(g, p2);
		//}

		protected void draw_halfspace(Graphics g, Halfspace h) {
			//
			//int sizeAsLevel;
			//sizeAsLevel = Trixel.EstimateLevelFromSize(h.RadiusInRadian);
			////
			//// A visual halfspace is a ring
			////
			//if (_zoom_level > sizeAsLevel + 8)
			//    return;

			int n = _ringsize;
			double theta = 0.0;
			double dtheta = 2.0 * Math.PI / (n - 1);
			double[,] mat = new double[3, 3];
			Cartesian startpoint = new Cartesian();
			Cartesian endpoint = new Cartesian();
			Point[] hsPoints = new Point[n];
			// Pen pen = new Pen(System.Drawing.Brushes.Aqua, 2.0f);
			theta = 0.0;
			//
			// Pick a point perpendicular to halfspace axis
			// pivot on the axis of component with smallest absolute value
			// choose one of (x, y, 0) or (x, 0, z) or (0, y, z)

			double x, y, z, norm;
			x = Math.Abs(h.Vector.X);
			y = Math.Abs(h.Vector.Y);
			z = Math.Abs(h.Vector.Z); // (x,y,z) is normalized
			int dim;
			if (x <= y && x <= z) {
				dim = 0;
			} else if (y <= x && y <= z) {
				dim = 1;
			} else {
				dim = 2;
			}
			switch (dim) {
				case 0:
					x = 0.0;
					y = -h.Vector.Z;
					z = h.Vector.Y;
					break;
				case 1:
					x = -h.Vector.Z;
					y = 0.0;
					z = h.Vector.X;
					break;
				default:
					x = -h.Vector.Y;
					y = h.Vector.X;
					z = 0.0;
					break;
			}
			//
			// (x,y,z) is perpendicular to h.sv, but not yet normalized
			// proper radius is sqrt(1 - d^2)
			//
			double rad = Math.Sqrt(1.0 - h.Cos0 * h.Cos0);
			norm = 1.0 / Math.Sqrt(x * x + y * y + z * z);
			norm *= rad;
			x *= norm;
			y *= norm;
			z *= norm;
			x += h.Cos0 * h.Vector.X;
			y += h.Cos0 * h.Vector.Y;
			z += h.Cos0 * h.Vector.Z;
			endpoint.Set(x, y, z, false);
			_camera.rotate(endpoint, ref startpoint);
			Cartesian axis = new Cartesian(h.Vector, false);
			_camera.rotate(h.Vector, ref axis);
			theta = 0.0;
			//
			//
			// Draw a segment, if at least one point is visible
			//
			int ox = -1, oy = -1, nx = -1, ny = -1;
			Point nPoint = new Point();

			for (int i = 0; i < n; i++) {
				theta += dtheta;
				Camera3D.getRotationMatrix(mat, axis, theta);
				Camera3D.rotate(startpoint, mat, ref endpoint);
				_camera.dw2vp(endpoint, ref nPoint);
				nx = nPoint.X;
				ny = nPoint.Y;
				if (i > 0 && endpoint.Z > 0.0) { // Cheap hidden line 
					g.DrawLine(_customPen, ox, oy, nx, ny);
				}
				ox = nx;
				oy = ny;
			}


			// pen.Dispose();
		}
        public void contents(Graphics g, PictureBox picture, WDB wdb) {
            contents(g, picture);
            if(wdb != null && _showduper) {
                foreach(WDB.Segment segg in wdb.coastline) {
                    if(segg.rank < 2) {
                        for(int i = 1; i < segg.npoints; i++) {
                            draw_WorldSegment(g, segg.points[i - 1], segg.points[i], false);
                        }
                    }
                }
                foreach(WDB.Segment segg in wdb.boundaries) {
                    if(segg.rank < 2) {
                        for(int i = 1; i < segg.npoints; i++) {
                            draw_WorldSegment(g, segg.points[i - 1], segg.points[i], false);
                        }
                    }
                }

            }
        }
		public void contents(Graphics g, PictureBox picture) {
			int w = picture.Width;
			int h = picture.Height;
			int n;
			//
			// Tell camera what we are at the moment
			_camera.set_size(w, h);
			//
			// Hope, that the rest speaks for itself
			// mouse is tracked by mousemove, which computes
			// the mouse_hid, trackMouse display the mouse_hid
			// as a trinagle. See trackMouse() if you need more
			// mousing around
			// Each call that produces some rendering of
			// a bunch of triangles does it
			// with the _pen.
			// Here, we set the _pen color first
			erase(g);
            //polar_rotate(0.02);
            view_rotate(twistAngle - _twistAngle, true);
            _twistAngle = twistAngle;
			if (_showgrid) {
				// _pen = System.Drawing.Pens.LightSlateGray; // you could simply pass renderstyle
                _customPen.Width = 0.1f;
                _customPen.Color = Color.LightSlateGray;
                _pen = _customPen;
				globalGrid(g, _grid_level);
			}
			//_pen = System.Drawing.Pens.Orange;
			//trackMouse(g);

			_pen = System.Drawing.Pens.AntiqueWhite;
			renderPickedHID(g);

			//renderHalfspace(g, _halfspace);
            footnote("{0,11:0,0}", this.tick);
            if (_showannotate) {
                annotate(g, picture, _captionTL.ToString(), _captionBR.ToString());
            }
            if(_showconvexes) {
                _customPen.Color = System.Drawing.Color.Orange;
                _customPen.Width = 2;
                n = scon.Count;
                foreach(Halfspace hs in scon.HalfspaceList) {
                    draw_halfspace(g, hs);
                }
                _customPen.Color = System.Drawing.Color.Red;
                _customPen.Width = 3;
                _halfspace.Vector.Set(1.0, 0.0, 0.0, false);
                draw_halfspace(g, _halfspace);
            }
			if (_showtrixels) {
				_pen = System.Drawing.Pens.White;
				n = _tracedHIDs.Count;
				if (n > 0) {
					for (int i = 0; i < n; i++) {
                        Int64 jid; bool full;
						jid = _tracedHIDs[i].hid;
                        full = _tracedHIDs[i].full;
                        if(full) {
                            draw_hid(g, jid, _curvetrixels, System.Drawing.Color.Tomato, 3.0f);
                        } else {
                            draw_hid(g, jid, _curvetrixels, System.Drawing.Color.White, 1.0f);
                        }
                    }
				}
			}
			//
			// Center hairline
			//
            // It't be cool for the centerline to show which way north is ...
            // 
			if (_showaxes) {
				int x, y;
				_pen = System.Drawing.Pens.Violet;
				_camera.dw2vp(0.0, 0.0, out x, out y);
				g.DrawArc(_pen, x - 2, y - 2, 4, 4, 0, 360);
				g.DrawLine(_pen, x, y + 2, x, y + 7);
				g.DrawLine(_pen, x, y - 2, x, y - 7);
				g.DrawLine(_pen, x + 2, y, x + 7, y);
				g.DrawLine(_pen, x - 2, y, x - 7, y);

			}
            if(!_showaxes) {
                draw_worldDot(g, new Cartesian(0.0, 10.0));
                draw_worldDot(g, new Cartesian(0.0, 0.0));
                //
                // Find out which way north is from here
            }
			if (_showaxes) {
				Cartesian xmark = new Cartesian(1.0, 0.0, 0.0, false);
				_pen = System.Drawing.Pens.Red;
				draw_worldDot(g, xmark);

				xmark.Set(0.0, 1.0, 0.0, false);
				_pen = System.Drawing.Pens.Green;
				draw_worldDot(g, xmark);

				xmark.Set(0.0, 0.0, 1.0, false);
				_pen = System.Drawing.Pens.Blue;
				draw_worldDot(g, xmark);

			}//
			// Make a convex, patch, and display
            //int arccount = 0;
//            if (_showduper) {
//                _pen = System.Drawing.Pens.White;
//                if (sreg != null) {
//                    Cartesian v0, v1, v2;
//                    draw_hid(g, 3085L, true); // forces yellow
//                    Trixel.ToTriangle(3085L, out v0, out v1, out v2);
////					draw_worldDot(g, v0);
////					draw_worldSq(g, v1);

//                    draw_worldSq(g, new Cartesian(0.989910532471754, -0.117043851642381, 0.0798615958656336, false));
//                    draw_worldSq(g, new Cartesian(0.987586804864801, -0.150073961190551, 0.0463692681580186, false));
//                    // +		root1	{0.989910532471754 -0.117043851642381 0.0798615958656336}
//                    // +		root2	{0.987586804864801 -0.150073961190551 0.0463692681580186}

//                    foreach (Convex c in sreg.ConvexList) {
//                        foreach (Patch p in c.PatchList) {
//                            foreach (Arc a in p.ArcList) {
//                                switch (arccount) {
//                                    case 0:
//                                        _customPen.Color = System.Drawing.Color.White;
//                                        _customPen.Width = 1;
//                                        draw_arc(g, a);
//                                        break;
//                                    case 1:
//                                        _customPen.Color = System.Drawing.Color.DeepPink;
//                                        _customPen.Width = 2;
//                                        draw_arc(g, a);
//                                        break;
//                                    case 2:
//                                        _customPen.Color = System.Drawing.Color.Yellow;
//                                        _customPen.Width = 4;
//                                        draw_arc(g, a);
//                                        break;
//                                }
								
//                                arccount++;
//                            }
//                        }
//                    }
//                }
//            }
			//if (false){
			//    HTM.Region r = new HTM.Region();
			//    Convex c = new Convex();
			//    c.halfspaceList.Add(new Halfspace(
			//        0.0, 0.0, 1.0, false, 0.984807753012208
			//        ));
			//    c.halfspaceList.Add(new Halfspace(
			//        0.0, 0.0, -1.0, false, -0.985109326154774
			//        ));
			//    c.halfspaceList.Add(new Halfspace(
			//        -0.4067366430758, 0.913545457642601, 0.0, false, 0.0
			//        ));
			//    c.halfspaceList.Add(new Halfspace(
			//        0.408330460381385, -0.912834177233043, 0.0, false, 0.0));

			//    r.convexList.Add(c);
			//    SuperRegion sr = new SuperRegion(r, false, false);
			//    Patch pat = sr.convexList[0].patchList[0];
			//    for (int i = 0; i < pat.arcList.Length; i++) {
			//        draw_halfspace(g, pat.mec);
			//        draw_worldDot(g, pat.arcList[i].p1);
			//        draw_worldDot(g, pat.arcList[i].p2);
			//    }
			//}
		}

		public void annotate(Graphics g, PictureBox picture, String caption, String footnote) {
			//
			// Caption is Top Left, Footnote is Bottom Right
			// by this program's definition
			//
			g.DrawString(caption,
				font, System.Drawing.Brushes.GreenYellow, _locationTL);
			_locationBR.X = picture.Width / 2 +72;
			_locationBR.Y = picture.Height - 16;
			
			g.DrawString(footnote,
				font, System.Drawing.Brushes.GreenYellow, _locationBR);

			// In case you need to put stuff on other corners:
			// g.DrawString("Top Right", font, System.Drawing.Brushes.Red, _locationBR.X, _locationTL.Y);
			// g.DrawString("Bot Left", font, System.Drawing.Brushes.Pink, _locationTL.X, _locationBR.Y);

			return;
		}
		/// <summary>
		/// Draw an arc from p1 to p2 via pmiddle. if p1 = p2, then full circle
		/// </summary>
		/// <param name="g"></param>
		/// <param name="p1"></param>
		/// <param name="p2"></param>
		/// <param name="pmiddle"></param>
		protected void draw_arc(Graphics g, Arc arc){
			if (arc.Point1.Same(arc.Point2, Const.Tolerance)) {
				// _customPen.Color = System.Drawing.Color.Cyan;
				draw_halfspace(g, arc.Circle);
			} else {
				
				double degrees, radians;
				Cartesian pout = new Cartesian(1.0, 0.0, 0.0, false);
				double[,] mat = new double[3, 3];

				degrees = Cover.angleInContext(arc.Point1, arc.Point2, arc.Circle);
				radians = degrees * Const.Degree2Radian;

				double theta = 0.0;
				Point[] arcPoints = new Point[20];
				double dt = radians / 19.0;

				for (int i = 0; i < 20; i++) {
					Camera3D.getRotationMatrix(mat, arc.Circle.Vector, theta);
					Camera3D.rotate(arc.Point1, mat, ref pout);
					_camera.rotate(pout, ref w0);
					_camera.dw2vp(w0, ref arcPoints[i]);
					theta += dt;
				}

				g.DrawLines(_customPen, arcPoints);
				//draw_worldDot(g, arc.middle);
				//draw_worldSq(g, arc.p2);

			}

		}
		protected void draw_hid(Graphics g, Int64 hid,
            bool arced, System.Drawing.Color color, float width) {
			//
			
			int level;
			Cartesian v0, v1, v2;
			//
			// len is length of text, which is level + 2
			//
			level = Trixel.LevelOfHid(hid);
			Trixel.ToTriangle(hid, out v0, out v1, out v2);
//			Trixel.NameToTriangle(name, out v0, out v1, out v2);
			// Draw this triangle only if the level of detail 
			// is consistent, that is level of triangle (l)
			// must be no greater than zoom level (z) + 7, 
			// and no less than z - 4;
			
			if (_zoom_level -4 < level && level < _zoom_level + 7) {
                this.draw_worldTriangle(g, v0, v1, v2, arced,
                    color, width);
			}
			//
			// Draw a dot is triangle level is too high
			if (level >= _zoom_level + 7) {
				this.draw_worldDot(g, v0, v1, v2);
			}
		}

		protected void update_time() {
			//lookat_ra += 231.0 * cam_ra_speed;
			//lookat_dec -= 52.0 * cam_dec_speed;
			//if (lookat_dec < -90.0) {
			//    lookat_dec += cam_dec_speed * 112.0;
			//    cam_dec_speed *= -1.0;
			//} else if (lookat_dec > 90.0) {
			//    lookat_dec += cam_dec_speed * 112.0;
			//    cam_dec_speed *= -1.0;
			//}
			//if (lookat_ra > 360) lookat_ra -= 360.0;
		}
		protected void draw_spinlogo(Graphics g, double s) {
			//
			//
			//
			int segs = 21;
			Point[] points = new Point[segs];
			double[] xarr = new double[segs];
			double[] yarr = new double[segs];
			double[] zarr = new double[segs];
			double dec = 0.0;
			double ra = 0.0;
			_camera.reset();
			// t3 makes things in unit sphere world
			// t3 can be rotated so that any ra,dec point is on the north pole
			// here, in Kanvas, we put the unit sphere world into the picture
			// 

			// ----------------- How big to make the rotatig globe
			// ----------------- and where to position it
			//
			// _t3.set_camera(lookat_ra, lookat_dec);
			for (dec = -80.0; dec < 80.0; dec += 20.0) {
				_camera.make_deccircle(dec, xarr, yarr, zarr, segs);
				//this.SSDrawLines(g, xarr, yarr, zarr, segs);
			}
			for (ra = 0.0; ra < 360.0; ra += 30.0) {
				_camera.make_racircle(ra, segs, xarr, yarr, zarr);
				//this.SSDrawLines(g, xarr, yarr, zarr, segs);
			}
		}
		// ///////////// Tracing related stuff
		public void clearHalfspaces() {
            scon.Clear();
			_tracedhalfspaces.Clear();
		}
		public void addHalfspace(double x, double y, double z, double D) {
			_tracedhalfspaces.Add(new Halfspace(x, y, z, true, D));
            scon.Add(new Halfspace(x, y, z, true, D));
		}
		public void clearTracedHIDs() {
			_tracedHIDs.Clear();
		}
		public void addHID(Int64 hid, bool full) {
			_tracedHIDs.Add(new TracedHid(hid, full));
		}
		public void clearSteps() {
			_algSteps.Clear();
		}
		public void addStep(Int64 hid, String reason){
			_algSteps.Add(new Step(hid, reason));
		}
		public void showgrid(bool flag) {
			_showgrid = flag;
		}
		public void showtrixels(bool flag) {
			_showtrixels = flag;
		}
		public void showconvexes(bool flag) {
			_showconvexes = flag;
		}
		public void showduper(bool flag) {
			_showduper = flag;
		}
		public bool showaxes {
			set { _showaxes = value; }
		}
        public bool showannotate {
            set { _showannotate = value; }
        }
        public bool curvetrixels {
            set {
                _curvetrixels = value;
            }
        }
		
	}
}
