|
|
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 |
|