Blob Blame Raw
using System;
using System.Collections.Generic;

namespace EllipseTruncate {
	public class ActivePoint {
		public Point point;
		public ActivePoint(double x, double y) {
			point = new Point(x, y);
		}
	}

    public class MainWindow : Gtk.Window {
        static public void Main() {
        	Gtk.Application.Init();
        	MainWindow win = new MainWindow();
			win.Show();
			win.Maximize();
        	Gtk.Application.Run();
        }
		
		Point cursor;
		Point offset;
		ActivePoint point;
		ActiveAngleRange rangeAdd;
		ActiveAngleRange rangeXor;
		ActiveAngleRange rangeSub;
		List<ActivePoint> points = new List<ActivePoint>();
		List<ActiveAngleRange> ranges = new List<ActiveAngleRange>();
		
		ActivePoint ellipse0 = new ActivePoint(500.0, 500.0),
		            ellipse1 = new ActivePoint(600.0, 500.0),
		            ellipse2 = new ActivePoint(500.0, 450.0);
		ActivePoint bounds0 = new ActivePoint(450.0, 500.0),
		            bounds1 = new ActivePoint(500.0, 700.0),
		            bounds2 = new ActivePoint(600.0, 550.0);
		ActiveAngleRange rangeA = new ActiveAngleRange(new Point(150.0, 150.0), 50.0);
		ActiveAngleRange rangeB = new ActiveAngleRange(new Point(400.0, 150.0), 50.0);
		
		public MainWindow(): base(Gtk.WindowType.Toplevel) {
			Events = Gdk.EventMask.KeyPressMask
				   | Gdk.EventMask.KeyReleaseMask
			       | Gdk.EventMask.ButtonPressMask
			       | Gdk.EventMask.ButtonReleaseMask
			       | Gdk.EventMask.ButtonMotionMask
			       | Gdk.EventMask.PointerMotionMask;
			ExtensionEvents = Gdk.ExtensionMode.All;
			rangeA.b = rangeB.a;
			rangeB.b = rangeA.a;
			points.AddRange(new ActivePoint[] {ellipse0, ellipse1, ellipse2, bounds0, bounds1, bounds2});
			ranges.AddRange(new ActiveAngleRange[] {rangeA, rangeB});
        }
        
        private bool refreshOnIdle()
        	{ QueueDraw(); return false; }
        private void Refresh() {
        	QueueDraw(); //GLib.Idle.Add(refreshOnIdle);
        }
        
        protected override bool OnDeleteEvent(Gdk.Event e) {
			Gtk.Application.Quit();
			return false;
		}

		protected override bool OnExposeEvent(Gdk.EventExpose e) {
            Cairo.Context context = Gdk.CairoHelper.Create(e.Window);

        	context.Save();
        	context.SetSourceRGBA(1.0, 1.0, 1.0, 1.0);
        	context.Rectangle(0, 0, Allocation.Width, Allocation.Height);
        	context.Fill();
        	context.Restore();
        	context.Save();
			context.Antialias = Cairo.Antialias.Gray;

			foreach(ActivePoint p in points) {
        		context.Save();
				context.SetSourceRGBA(0.0, point == p ? 1.0 : 0.0, 1.0, 1.0);
				context.Arc(p.point.x, p.point.y, 5.0, 0, 2.0*Math.PI);
				context.Fill();
        		context.Restore();
			}

			{ // draw bounds
				Point p0 = bounds0.point, p1 = bounds1.point, p2 = bounds2.point;
        		context.Save();
				context.SetSourceRGBA(0.5, 0.5, 0.5, 0.2);
				context.LineWidth = 2.0;
				context.MoveTo(p0.x, p0.y);
				context.LineTo(p1.x, p1.y);
				context.LineTo(p1.x + p2.x - p0.x, p1.y + p2.y - p0.y);
				context.LineTo(p2.x, p2.y);
				context.ClosePath();
				context.Stroke();
        		context.Restore();
			}
        	
			// draw ellipse
			Ellipse ellipse = new Ellipse(ellipse0.point, ellipse1.point, ellipse2.point);
			ellipse.drawFull(context);
			ellipse.drawTruncated(context, bounds0.point, bounds1.point, bounds2.point);
			
			// draw concentric grid
			ConcentricGrid cg = new ConcentricGrid(
				ellipse, 20.0, bounds0.point, bounds1.point, bounds2.point );
			cg.draw(context);
			
			// draw ranges
			foreach(ActiveAngleRange rl in ranges) {
				rl.draw(context);
				if ((cursor - rl.point).len() < 2.0*rl.radius || !rl.current.isEmpty()) {
					context.Save();
					context.SetSourceRGBA(0.0, 0.0, 0.0, 0.5);
					context.LineWidth = 1.0;
					context.MoveTo(rl.point.x, rl.point.y);
					context.LineTo(cursor.x, cursor.y);
					context.Stroke();
					context.Restore();
				}					
			}
			
        	context.Restore();
            context.Dispose();
			return false;
		}
		
		private void releaseButton() {
			if (rangeAdd != null) rangeAdd.add();
			if (rangeXor != null) rangeXor.xor();
			if (rangeSub != null) rangeSub.subtract();
			point = null;
			rangeAdd = null;
			rangeXor = null;
			rangeSub = null;
		}

		protected override bool OnButtonPressEvent(Gdk.EventButton e) {
			cursor = new Point(e.X, e.Y);
			releaseButton();

			ActivePoint ap = null;
			Point o = new Point();
		    foreach(ActivePoint p in points)
				if ((p.point - cursor).len() <= 5.0)
					{ o = p.point - cursor; ap = p; }
			ActiveAngleRange ar = null;
			uint a0 = 0;
		    foreach(ActiveAngleRange r in ranges)
				if ((r.point - cursor).len() < 2.0*r.radius)
					{ ar = r; a0 = AngleRange.toUIntDiscrete((cursor - r.point).atan()); }

			if (e.Button == 1) {
				if (ap != null) {
					point = ap;
					offset = o;
				} else
				if (ar != null) {
					ar.current = new AngleRange.Entry();
					ar.current.a0 = a0;
					rangeAdd = ar;
				}
			} else
			if (e.Button == 2) {
				if (ar != null) {
					ar.current = new AngleRange.Entry();
					ar.current.a0 = a0;
					rangeXor = ar;
				}
			} else
			if (e.Button == 3) {
				if (ar != null) {
					ar.current = new AngleRange.Entry();
					ar.current.a0 = a0;
					rangeSub = ar;
				}
			}
			
			Refresh();
			return false;
		}

		protected override bool OnButtonReleaseEvent(Gdk.EventButton e) {
			cursor = new Point(e.X, e.Y);
			releaseButton();
			Refresh();
			return false;
		}

		protected override bool OnMotionNotifyEvent(Gdk.EventMotion e) {
			cursor = new Point(e.X, e.Y);
			if (point != null) point.point = cursor + offset;
			if (rangeAdd != null)
				rangeAdd.current.a1 = AngleRange.toUIntDiscrete((cursor - rangeAdd.point).atan());
			if (rangeXor != null)
				rangeXor.current.a1 = AngleRange.toUIntDiscrete((cursor - rangeXor.point).atan());
			if (rangeSub != null)
				rangeSub.current.a1 = AngleRange.toUIntDiscrete((cursor - rangeSub.point).atan());
			Refresh();
			return false;
		}
    }
}