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);
}
}
}
}