From a79478f0d4825112a2a2ff34e62618f65d5fc1de Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: May 24 2018 14:15:21 +0000 Subject: ellipse truncate: ConcentricGrid --- 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,