Blame mono/EllipseTruncate/Ellipse.cs

4ef489
using System;
782bf6
using System.Collections.Generic;
4ef489
4ef489
namespace EllipseTruncate {
4ef489
	public class Ellipse {
4ef489
		public Matrix matrix;
4ef489
		public Matrix matrixInv;
4ef489
	
4ef489
		public Ellipse(Point p0, Point p1, Point p2) {
4ef489
			Point d = p1 - p0;
a79478
			double r1 = d.len();
a79478
			double r2 = Math.Abs( (d.rotate90()*(p2 - p0))/r1 );
4ef489
			matrix = Matrix.identity()
a79478
					.translate(p0)
a79478
			        .rotate(d.atan())
4ef489
			        .scale(r1, r2);
4ef489
			matrixInv = matrix.invert();
4ef489
		}
a79478
a79478
		public Ellipse(Matrix matrix) {
a79478
			this.matrix = matrix;
a79478
			matrixInv = matrix.invert();
a79478
		}
a79478
				
a79478
		public Ellipse(Matrix matrix, Matrix matrixInv) {
a79478
			this.matrix = matrix;
a79478
			this.matrixInv = matrixInv;
a79478
		}
a79478
a79478
		public Ellipse(Ellipse ellipse):
a79478
			this(ellipse.matrix, ellipse.matrixInv) { }
4ef489
		
a79478
		public void drawFull(Cairo.Context context, bool grid = false) {
4ef489
    		int segments = 100;
4ef489
    		double da = 2.0*Math.PI/segments;
4ef489
    		double s = Math.Sin(da);
4ef489
    		double c = Math.Cos(da);
4ef489
    		Point r = new Point(1.0, 0.0);
4ef489
a79478
			if (!grid) {
a79478
				context.Save();
a79478
				context.Matrix = (new Matrix(context.Matrix)*matrix).toCairo();
a79478
				context.SetSourceRGBA(1.0, 0.0, 0.0, 0.1);
a79478
				context.Arc(0.0, 0.0, 1.0, 0.0, 2.0*Math.PI);
a79478
				context.ClosePath();
a79478
				context.Fill();
a79478
				context.Restore();
a79478
			}
4ef489
4ef489
    		context.Save();
4ef489
			context.LineWidth = 0.5;
4ef489
			context.SetSourceRGBA(1.0, 0.0, 0.0, 0.5);
a79478
			if (grid) context.SetSourceRGBA(0.0, 0.0, 0.0, 0.2);
4ef489
			Point p = matrix*r;
4ef489
			for(int i = 0; i < segments; ++i) {
4ef489
				r = new Point(r.x*c - r.y*s, r.y*c + r.x*s);
4ef489
  				p = matrix*r;
4ef489
				context.LineTo(p.x, p.y);
4ef489
			}
4ef489
			context.ClosePath();
4ef489
			context.Stroke();
4ef489
    		context.Restore();
4ef489
		}
4ef489
		
4ef489
		private static void swap(ref double a, ref double b)
4ef489
			{ double c = a; a = b; b = c; }
4ef489
		private static void swap(ref int a, ref int b)
4ef489
			{ int c = a; a = b; b = c; }
4ef489
782bf6
		private bool cutRange(AngleRange range, uint da, double h) {
782bf6
			if (h <= Geometry.precision - 1.0)
4ef489
				return true;
782bf6
			if (h >= 1.0 - Geometry.precision)
4ef489
				return false;
782bf6
			uint a = AngleRange.toUInt(Math.Asin(h));
782bf6
			range.subtract(new AngleRange.Entry(
782bf6
				da - a, (da + a)^AngleRange.half ));
782bf6
			return !range.isEmpty();
4ef489
		}
4ef489
		
782bf6
		public void putSegment(Cairo.Context context, double da, double s, double c, double a0, double a1) {
782bf6
			int cnt = (int)Math.Floor((a1 - a0)/da);
782bf6
			Point r = new Point(1.0, 0.0).rotate(a0);
782bf6
			Point p = matrix*r;
782bf6
			context.MoveTo(p.x, p.y);
782bf6
			for(int j = 0; j < cnt; ++j) {
782bf6
				r = new Point(r.x*c - r.y*s, r.y*c + r.x*s);
782bf6
  				p = matrix*r;
782bf6
				context.LineTo(p.x, p.y);
782bf6
			}
782bf6
			r = new Point(1.0, 0.0).rotate(a1);
782bf6
			p = matrix*r;
782bf6
			context.LineTo(p.x, p.y);
782bf6
		}
782bf6
a79478
		public void drawTruncated(Cairo.Context context, Point b0, Point b1, Point b2, bool grid = false) {
4ef489
			Point dx = matrixInv.turn(b1 - b0);
4ef489
			Point dy = matrixInv.turn(b2 - b0);
4ef489
			Point nx = dx.rotate90().normalize();
4ef489
			Point ny = dy.rotate90().normalize();
4ef489
			Point o = matrixInv*b0;
4ef489
			
782bf6
			uint ax = AngleRange.toUInt(dx.atan());
782bf6
			uint ay = AngleRange.toUInt(dy.atan());
4ef489
4ef489
			double sign = nx*dy;
4ef489
			if (Math.Abs(sign) <= Geometry.precision) return;
4ef489
			if (sign < 0.0) {
4ef489
				nx *= -1.0; ny *= -1.0;
782bf6
				ax ^= AngleRange.half; ay ^= AngleRange.half;
4ef489
			}
4ef489
a79478
			if (!grid) {
a79478
				// draw bounds
a79478
				context.Save();
a79478
				context.Matrix = (new Matrix(context.Matrix)*matrix).toCairo();
a79478
				context.SetSourceRGBA(1.0, 0.0, 0.0, 0.1);
a79478
				context.MoveTo(o.x, o.y);
a79478
				context.RelLineTo(dx.x, dx.y);
a79478
				context.RelLineTo(dy.x, dy.y);
a79478
				context.RelLineTo(-dx.x, -dx.y);
a79478
				context.ClosePath();
a79478
				context.Fill();
a79478
				context.Restore();
a79478
			}
4ef489
			
782bf6
			// build ranges
782bf6
			AngleRange range = new AngleRange(true);
782bf6
			if ( !cutRange(range, ax, o*nx) ) return;
782bf6
			if ( !cutRange(range, ax^AngleRange.half, -((o+dx+dy)*nx)) ) return;
782bf6
			if ( !cutRange(range, ay, (o+dx)*ny) ) return;
782bf6
			if ( !cutRange(range, ay^AngleRange.half, -((o+dy)*ny)) ) return;
4ef489
			
4ef489
			// draw
4ef489
    		int segments = 100;
4ef489
    		double da = 2.0*Math.PI/segments;
4ef489
    		double s = Math.Sin(da);
4ef489
    		double c = Math.Cos(da);
4ef489
4ef489
    		context.Save();
4ef489
			context.LineWidth = 2.0;
4ef489
			context.SetSourceRGBA(0.0, 0.0, 1.0, 1.0);
a79478
			if (grid) context.LineWidth = 1.0;
a79478
			if (grid) context.SetSourceRGBA(0.0, 0.0, 0.0, 1.0);
782bf6
			if (range.isFull()) {
782bf6
				putSegment(context, da, s, c, 0.0, AngleRange.period);
782bf6
			} else {
782bf6
				bool f = range.flip;
782bf6
				uint prev = range.angles[range.angles.Count - 1];
782bf6
				foreach(uint a in range.angles) {
782bf6
					if (f) {
782bf6
						double a0 = AngleRange.toDouble(prev);
782bf6
						double a1 = AngleRange.toDouble(a);
782bf6
						if (a < prev) a1 += AngleRange.period;
782bf6
						putSegment(context, da, s, c, a0, a1);
782bf6
					}
782bf6
					prev = a; f = !f;
4ef489
				}
4ef489
			}
4ef489
			context.Stroke();
4ef489
			context.Restore();
4ef489
		}
4ef489
	}
4ef489
}
4ef489