diff --git a/mono/EllipseTruncate/ConcentricGrid.cs b/mono/EllipseTruncate/ConcentricGrid.cs
new file mode 100644
index 0000000..b6ef950
--- /dev/null
+++ b/mono/EllipseTruncate/ConcentricGrid.cs
@@ -0,0 +1,72 @@
+using System;
+
+namespace EllipseTruncate {
+ public class ConcentricGrid {
+ Ellipse ellipse;
+ double step;
+ Point b0, b1, b2;
+
+ public ConcentricGrid(Ellipse ellipse, double step, Point b0, Point b1, Point b2) {
+ this.ellipse = ellipse;
+ this.step = step;
+ this.b0 = b0; this.b1 = b1; this.b2 = b2;
+ }
+
+ public void calcBounds(out double min, out double max) {
+ Point o = ellipse.matrixInv*b0;
+ Point dx = ellipse.matrixInv.turn(b1 - b0);
+ Point dy = ellipse.matrixInv.turn(b2 - b0);
+ max = 0.0;
+ min = double.PositiveInfinity;
+
+ // distance to points
+ Point[] corners = new Point[] { o, o+dx, o+dx+dy, o+dy };
+ foreach(Point p in corners) {
+ double k = p.len();
+ if (k < min) min = k;
+ if (k > max) max = k;
+ }
+
+ // distance to sides
+ Point[] lines = new Point[] { dx, dy, -1.0*dx, -1.0*dy };
+ int positive = 0, negative = 0;
+ for(int i = 0; i < 4; ++i) {
+ double len2 = lines[i].lenSqr();
+ if (len2 <= Geometry.precisionSqr) continue;
+ double k = corners[i]*lines[i].rotate90()/Math.Sqrt(len2);
+ if (k > Geometry.precision) ++positive;
+ if (k < Geometry.precision) ++negative;
+ double l = -(corners[i]*lines[i]);
+ if (l <= Geometry.precision || l >= len2 - Geometry.precision) continue;
+ k = Math.Abs(k);
+ if (k < min) min = k;
+ if (k > max) max = k;
+ }
+
+ // if center is inside bounds
+ if (min < 0.0 || positive == 0 || negative == 0) min = 0.0;
+ }
+
+ public void draw(Cairo.Context context) {
+ double r2 = Math.Min(
+ ellipse.matrix.row0().lenSqr(),
+ ellipse.matrix.row1().lenSqr() );
+ if (r2 <= Geometry.precisionSqr) return;
+ double actualStep = step/Math.Sqrt(r2);
+
+ double min, max;
+ calcBounds(out min, out max);
+ if (max <= min) return;
+ if (max - min > 1e5) return;
+ int minI = (int)Math.Ceiling((min - 1.0)/actualStep + Geometry.precision);
+ int maxI = (int)Math.Ceiling((max - 1.0)/actualStep - Geometry.precision);
+ for(int i = minI; i < maxI; ++i) {
+ double scale = i*actualStep + 1.0;
+ Ellipse e = new Ellipse( ellipse.matrix.scale(scale) );
+ e.drawFull(context, true);
+ e.drawTruncated(context, b0, b1, b2, true);
+ }
+ }
+ }
+}
+
diff --git a/mono/EllipseTruncate/Ellipse.cs b/mono/EllipseTruncate/Ellipse.cs
index f6b9e08..06af65a 100644
--- a/mono/EllipseTruncate/Ellipse.cs
+++ b/mono/EllipseTruncate/Ellipse.cs
@@ -3,51 +3,54 @@ using System.Collections.Generic;
namespace EllipseTruncate {
public class Ellipse {
- public Point center;
- public Point p1;
- public Point p2;
- public double r1;
- public double r2;
- public double angle;
-
public Matrix matrix;
public Matrix matrixInv;
public Ellipse(Point p0, Point p1, Point p2) {
- center = p0;
- this.p1 = p1;
Point d = p1 - p0;
- r1 = d.len();
- angle = d.atan();
- Point dp = d.rotate90()/r1;
- r2 = Math.Abs(dp*(p2 - p0));
- this.p2 = p0 + dp*r2;
-
+ double r1 = d.len();
+ double r2 = Math.Abs( (d.rotate90()*(p2 - p0))/r1 );
matrix = Matrix.identity()
- .translate(center)
- .rotate(angle)
+ .translate(p0)
+ .rotate(d.atan())
.scale(r1, r2);
matrixInv = matrix.invert();
}
+
+ public Ellipse(Matrix matrix) {
+ this.matrix = matrix;
+ matrixInv = matrix.invert();
+ }
+
+ public Ellipse(Matrix matrix, Matrix matrixInv) {
+ this.matrix = matrix;
+ this.matrixInv = matrixInv;
+ }
+
+ public Ellipse(Ellipse ellipse):
+ this(ellipse.matrix, ellipse.matrixInv) { }
- public void drawFull(Cairo.Context context) {
+ public void drawFull(Cairo.Context context, bool grid = false) {
int segments = 100;
double da = 2.0*Math.PI/segments;
double s = Math.Sin(da);
double c = Math.Cos(da);
Point r = new Point(1.0, 0.0);
- context.Save();
- context.Matrix = (new Matrix(context.Matrix)*matrix).toCairo();
- context.SetSourceRGBA(1.0, 0.0, 0.0, 0.1);
- context.Arc(0.0, 0.0, 1.0, 0.0, 2.0*Math.PI);
- context.ClosePath();
- context.Fill();
- context.Restore();
+ if (!grid) {
+ context.Save();
+ context.Matrix = (new Matrix(context.Matrix)*matrix).toCairo();
+ context.SetSourceRGBA(1.0, 0.0, 0.0, 0.1);
+ context.Arc(0.0, 0.0, 1.0, 0.0, 2.0*Math.PI);
+ context.ClosePath();
+ context.Fill();
+ context.Restore();
+ }
context.Save();
context.LineWidth = 0.5;
context.SetSourceRGBA(1.0, 0.0, 0.0, 0.5);
+ if (grid) context.SetSourceRGBA(0.0, 0.0, 0.0, 0.2);
Point p = matrix*r;
for(int i = 0; i < segments; ++i) {
r = new Point(r.x*c - r.y*s, r.y*c + r.x*s);
@@ -90,7 +93,7 @@ namespace EllipseTruncate {
context.LineTo(p.x, p.y);
}
- public void drawTruncated(Cairo.Context context, Point b0, Point b1, Point b2) {
+ public void drawTruncated(Cairo.Context context, Point b0, Point b1, Point b2, bool grid = false) {
Point dx = matrixInv.turn(b1 - b0);
Point dy = matrixInv.turn(b2 - b0);
Point nx = dx.rotate90().normalize();
@@ -107,16 +110,19 @@ namespace EllipseTruncate {
ax ^= AngleRange.half; ay ^= AngleRange.half;
}
- context.Save();
- context.Matrix = (new Matrix(context.Matrix)*matrix).toCairo();
- context.SetSourceRGBA(1.0, 0.0, 0.0, 0.1);
- context.MoveTo(o.x, o.y);
- context.RelLineTo(dx.x, dx.y);
- context.RelLineTo(dy.x, dy.y);
- context.RelLineTo(-dx.x, -dx.y);
- context.ClosePath();
- context.Fill();
- context.Restore();
+ if (!grid) {
+ // draw bounds
+ context.Save();
+ context.Matrix = (new Matrix(context.Matrix)*matrix).toCairo();
+ context.SetSourceRGBA(1.0, 0.0, 0.0, 0.1);
+ context.MoveTo(o.x, o.y);
+ context.RelLineTo(dx.x, dx.y);
+ context.RelLineTo(dy.x, dy.y);
+ context.RelLineTo(-dx.x, -dx.y);
+ context.ClosePath();
+ context.Fill();
+ context.Restore();
+ }
// build ranges
AngleRange range = new AngleRange(true);
@@ -134,6 +140,8 @@ namespace EllipseTruncate {
context.Save();
context.LineWidth = 2.0;
context.SetSourceRGBA(0.0, 0.0, 1.0, 1.0);
+ if (grid) context.LineWidth = 1.0;
+ if (grid) context.SetSourceRGBA(0.0, 0.0, 0.0, 1.0);
if (range.isFull()) {
putSegment(context, da, s, c, 0.0, AngleRange.period);
} else {
diff --git a/mono/EllipseTruncate/EllipseTruncate.csproj b/mono/EllipseTruncate/EllipseTruncate.csproj
index 352161f..ac91598 100644
--- a/mono/EllipseTruncate/EllipseTruncate.csproj
+++ b/mono/EllipseTruncate/EllipseTruncate.csproj
@@ -43,6 +43,7 @@
+
diff --git a/mono/EllipseTruncate/MainWindow.cs b/mono/EllipseTruncate/MainWindow.cs
index 05aa102..79edbb7 100644
--- a/mono/EllipseTruncate/MainWindow.cs
+++ b/mono/EllipseTruncate/MainWindow.cs
@@ -99,6 +99,11 @@ namespace EllipseTruncate {
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);
diff --git a/mono/EllipseTruncate/Matrix.cs b/mono/EllipseTruncate/Matrix.cs
index 272404f..6f848d1 100644
--- a/mono/EllipseTruncate/Matrix.cs
+++ b/mono/EllipseTruncate/Matrix.cs
@@ -81,6 +81,8 @@ namespace EllipseTruncate {
{ return this*scaling(x, y); }
public Matrix scale(Point s)
{ return this*scaling(s); }
+ public Matrix scale(double s)
+ { return this*scaling(s, s); }
public Point transform(Point p) {
return new Point( m00*p.x + m10*p.y + m20,