diff --git a/mono/EllipseTruncate/Ellipse.cs b/mono/EllipseTruncate/Ellipse.cs new file mode 100644 index 0000000..33a8f6d --- /dev/null +++ b/mono/EllipseTruncate/Ellipse.cs @@ -0,0 +1,215 @@ +using System; + +namespace EllipseTruncate { + public struct AngleRange { + public double angle, size; + + public AngleRange(double angle, double size) + { this.angle = angle; this.size = size; } + public bool isEmpty() + { return size <= Geometry.precision; } + public bool isFull() + { return size >= 2.0*Math.PI - Geometry.precision; } + public double end() + { return wrap(angle + size); } + public bool contains(double a) + { return diff(a, angle) < size; } + public bool intersects(AngleRange r) + { return contains(r.angle) || r.contains(angle); } + public AngleRange union(AngleRange r) { + double da = diff(r.angle, angle); + if (da <= size + Geometry.precision) + return new AngleRange(angle, Math.Max(size, da + r.size)); + da = 2.0*Math.PI - da; + if (da <= r.size + Geometry.precision) + return new AngleRange(r.angle, Math.Max(r.size, da + size)); + da = Math.Min(angle, r.angle); + return new AngleRange(da, Math.Max(angle + size, r.angle + r.size) - da); + } + + public static AngleRange byAngles(double a0, double a1) + { return new AngleRange(a0, diff(a1, a0)); } + public static double diff(double a1, double a0) + { return a0 < a1 ? a1 - a0 : a1 - a0 + 2.0*Math.PI; } + public static double wrap(double angle) { + return angle > Math.PI ? angle - 2.0*Math.PI + : angle < -Math.PI ? angle + 2.0*Math.PI + : angle; + } + } + + 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; + + matrix = Matrix.identity() + .translate(center) + .rotate(angle) + .scale(r1, r2); + matrixInv = matrix.invert(); + } + + public void drawFull(Cairo.Context context) { + 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(); + + context.Save(); + context.LineWidth = 0.5; + context.SetSourceRGBA(1.0, 0.0, 0.0, 0.5); + 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); + p = matrix*r; + context.LineTo(p.x, p.y); + } + context.ClosePath(); + context.Stroke(); + context.Restore(); + } + + private static void swap(ref double a, ref double b) + { double c = a; a = b; b = c; } + private static void swap(ref int a, ref int b) + { int c = a; a = b; b = c; } + + private bool cutRange(ref int index, AngleRange[] ranges, double da, double h) { + if (h <= Geometry.precision - 1.0) { + return true; + } else + if (h >= 1.0 - Geometry.precision) { + return false; + } else { + double a = Math.Asin(h); + ranges[index].angle = AngleRange.wrap( a < 0.0 + ? da - a - Math.PI + : da - a + Math.PI ); + ranges[index].size = Math.PI + a + a; + int j = index; + for(int i = 0, r = 0; i < index;) { + if (r > 0) ranges[i] = ranges[i + r]; + if (ranges[i].intersects(ranges[j])) { + if (j < i) { ranges[j] = ranges[i].union(ranges[j]); ++r; --index; } + else { ranges[i] = ranges[i].union(ranges[j]); j = i; ++i; } + if (ranges[j].isFull()) + return false; + } else ++i; + } + if (j == index) ++index; + } + return true; + } + + public void drawTruncated(Cairo.Context context, Point b0, Point b1, Point b2) { + Point dx = matrixInv.turn(b1 - b0); + Point dy = matrixInv.turn(b2 - b0); + Point nx = dx.rotate90().normalize(); + Point ny = dy.rotate90().normalize(); + Point o = matrixInv*b0; + + double ax = dx.atan(); + double ay = dy.atan(); + double aax = AngleRange.wrap(ax + Math.PI); + double aay = AngleRange.wrap(ay + Math.PI); + + double sign = nx*dy; + if (Math.Abs(sign) <= Geometry.precision) return; + if (sign < 0.0) { + double a; + nx *= -1.0; ny *= -1.0; + a = ax; ax = aax; aax = a; + a = ay; ay = aay; aay = a; + } + + 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(); + + // gather invisible ranges + AngleRange[] cutRanges = new AngleRange[4]; + int count = 0; + if ( !cutRange(ref count, cutRanges, ax , (o*nx)) ) return; + if ( !cutRange(ref count, cutRanges, aax, -((o+dx+dy)*nx)) ) return; + if ( !cutRange(ref count, cutRanges, ay , (o+dx)*ny) ) return; + if ( !cutRange(ref count, cutRanges, aay, -((o+dy)*ny)) ) return; + + // sort bounds + for(int i = 0; i < count; ++i) + for(int j = i+1; j < count; ++j) + if (cutRanges[j].angle < cutRanges[i].angle) + { AngleRange r = cutRanges[i]; cutRanges[i] = cutRanges[j]; cutRanges[j] = r; } + + // invert bounds + AngleRange[] ranges = new AngleRange[4]; + for(int i = 0; i < count; ++i) + ranges[i] = AngleRange.byAngles(cutRanges[(i > 0 ? i : count) - 1].end(), cutRanges[i].angle); + + // dummy + if (count == 0) + ranges[count++] = new AngleRange(0.0, 2.0*Math.PI); + + // draw + int segments = 100; + double da = 2.0*Math.PI/segments; + double s = Math.Sin(da); + double c = Math.Cos(da); + + context.Save(); + context.LineWidth = 2.0; + context.SetSourceRGBA(0.0, 0.0, 1.0, 1.0); + for(int i = 0; i < count; ++i) { + double angle = ranges[i].angle; + double size = ranges[i].size; + int cnt = (int)Math.Floor(size/da); + Point r = new Point(1.0, 0.0).rotate(angle); + Point p = matrix*r; + context.MoveTo(p.x, p.y); + for(int j = 0; j < cnt; ++j) { + r = new Point(r.x*c - r.y*s, r.y*c + r.x*s); + p = matrix*r; + context.LineTo(p.x, p.y); + } + r = new Point(1.0, 0.0).rotate(angle + size); + p = matrix*r; + context.LineTo(p.x, p.y); + } + context.Stroke(); + context.Restore(); + } + } +} + diff --git a/mono/EllipseTruncate/EllipseTruncate.csproj b/mono/EllipseTruncate/EllipseTruncate.csproj new file mode 100644 index 0000000..3f7e0a9 --- /dev/null +++ b/mono/EllipseTruncate/EllipseTruncate.csproj @@ -0,0 +1,66 @@ + + + + Debug + x86 + 10.0.0 + 2.0 + {A7CD6953-34F0-4125-9DF5-957ECF0E23F0} + Exe + EllipseTruncate + EllipseTruncate + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + x86 + false + + + none + true + bin\Release + prompt + 4 + x86 + false + + + + + + + + Rectangle %28copy%29.cs + + + + + + + + + False + gtk-sharp-2.0 + + + False + gtk-sharp-2.0 + + + False + glib-sharp-2.0 + + + False + gtk-sharp-2.0 + + + + \ No newline at end of file diff --git a/mono/EllipseTruncate/EllipseTruncate.sln b/mono/EllipseTruncate/EllipseTruncate.sln new file mode 100644 index 0000000..ff1a262 --- /dev/null +++ b/mono/EllipseTruncate/EllipseTruncate.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{AAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EllipseTruncate", "EllipseTruncate.csproj", "{A7CD6953-34F0-4125-9DF5-957ECF0E23F0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A7CD6953-34F0-4125-9DF5-957ECF0E23F0}.Debug|x86.ActiveCfg = Debug|x86 + {A7CD6953-34F0-4125-9DF5-957ECF0E23F0}.Debug|x86.Build.0 = Debug|x86 + {A7CD6953-34F0-4125-9DF5-957ECF0E23F0}.Release|x86.ActiveCfg = Release|x86 + {A7CD6953-34F0-4125-9DF5-957ECF0E23F0}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = EllipseTruncate.csproj + EndGlobalSection +EndGlobal diff --git a/mono/EllipseTruncate/Geometry.cs b/mono/EllipseTruncate/Geometry.cs new file mode 100644 index 0000000..b04234a --- /dev/null +++ b/mono/EllipseTruncate/Geometry.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace EllipseTruncate { + public static class Geometry { + public static readonly double precision = 1e-8; + public static readonly double subPrecision = precision*0.1; + public static readonly double precisionSqr = precision*precision; + public static readonly double sqrt2Pi = Math.Sqrt(2.0*Math.PI); + + public static bool isEqual(double a, double b) + { return Math.Abs(b - a) <= precision; } + public static bool isLess(double a, double b) + { return a - precision < b; } + public static bool isGreater(double a, double b) + { return b - precision < a; } + public static bool isLessOrEqual(double a, double b) + { return a + precision <= b; } + public static bool isGreaterOrEqual(double a, double b) + { return b + precision <= a; } + + public static double logNormalDistribuitionUnscaled(double x, double x0, double w) { + return Math.Exp(-0.5*Math.Pow(Math.Log(x/x0)/w, 2.0))/x; + } + + public static double logNormalDistribuition(double x, double x0, double w) { + return logNormalDistribuition(x, x0, w)/(w*sqrt2Pi); + } + + public static void truncateInfiniteLine(Rectangle bounds, ref Point p0, ref Point p1) { + if (p0.isEqual(p1)) return; + Point d = p0 - p1; + if (Math.Abs(d.x)*bounds.height > bounds.width*Math.Abs(d.y)) { + // horizontal + double k = d.y/d.x; + p1 = new Point(bounds.x1, p0.y + k*(bounds.x1 - p0.x)); + p0 = new Point(bounds.x0, p0.y + k*(bounds.x0 - p0.x)); + } else { + // vertical + double k = d.x/d.y; + p1 = new Point(p0.x + k*(bounds.y1 - p0.y), bounds.y1); + p0 = new Point(p0.x + k*(bounds.y0 - p0.y), bounds.y0); + } + } + + public static class Interpolation { + public delegate T HalfFunc(T a, double b); + public delegate T FullFunc(T a, T b); + + public static FullFunc add; + public static FullFunc sub; + public static HalfFunc mul; + public static HalfFunc div; + + static Interpolation() { + { // add + ParameterExpression a = Expression.Parameter(typeof(T)); + ParameterExpression b = Expression.Parameter(typeof(T)); + add = Expression.Lambda(Expression.Add(a, b), a, b).Compile(); + } + { // sub + ParameterExpression a = Expression.Parameter(typeof(T)); + ParameterExpression b = Expression.Parameter(typeof(T)); + sub = Expression.Lambda(Expression.Subtract(a, b), a, b).Compile(); + } + { // mul + ParameterExpression a = Expression.Parameter(typeof(T)); + ParameterExpression b = Expression.Parameter(typeof(double)); + mul = Expression.Lambda(Expression.Multiply(a, b), a, b).Compile(); + } + { // div + ParameterExpression a = Expression.Parameter(typeof(T)); + ParameterExpression b = Expression.Parameter(typeof(double)); + div = Expression.Lambda(Expression.Divide(a, b), a, b).Compile(); + } + } + + public static T linear(T p0, T p1, double l) + { return add(mul(p0, 1.0-l), mul(p1, l)); } + + public static T spline(T p0, T p1, T t0, T t1, double l) { + double ll = l*l; + double lll = ll*l; + return add( add( mul(p0, ( 2.0*lll - 3.0*ll + 1.0)), + mul(p1, (-2.0*lll + 3.0*ll )) ), + add( mul(t0, ( lll - 2.0*ll + l )), + mul(t1, ( lll - 1.0*ll )) )); + } + + public static T splineTangent(T p0, T p1, T t0, T t1, double l) { + double ll = l*l; + return add( mul(sub(p0, p1), 6.0*(ll - l)), + add( mul(t0, ( 3.0*ll - 4.0*l + 1.0)), + mul(t1, ( 3.0*ll - 2.0*l )) )); + } + } + + public static double interpolationLinear(double p0, double p1, double l) + { return p0*(1.0 - l) + p1*l; } + + public static double interpolationSpline(double p0, double p1, double t0, double t1, double l) { + double ll = l*l; + double lll = ll*l; + return p0*( 2.0*lll - 3.0*ll + 1.0) + + p1*(-2.0*lll + 3.0*ll ) + + t0*( lll - 2.0*ll + l ) + + t1*( lll - 1.0*ll ); + } + + public static double interpolationSplineTangent(double p0, double p1, double t0, double t1, double l) { + double ll = l*l; + return (p0 - p1)*6.0*(ll - l) + + t0*( 3.0*ll - 4.0*l + 1.0) + + t1*( 3.0*ll - 2.0*l ); + } + + public static Point interpolationLinear(Point p0, Point p1, double l) + { return p0*(1.0 - l) + p1*l; } + + public static Point interpolationSpline(Point p0, Point p1, Point t0, Point t1, double l) { + double ll = l*l; + double lll = ll*l; + return p0*( 2.0*lll - 3.0*ll + 1.0) + + p1*(-2.0*lll + 3.0*ll ) + + t0*( lll - 2.0*ll + l ) + + t1*( lll - 1.0*ll ); + } + + public static Point interpolationSplineTangent(Point p0, Point p1, Point t0, Point t1, double l) { + double ll = l*l; + return (p0 - p1)*6.0*(ll - l) + + t0*( 3.0*ll - 4.0*l + 1.0) + + t1*( 3.0*ll - 2.0*l ); + } + } +} diff --git a/mono/EllipseTruncate/MainWindow.cs b/mono/EllipseTruncate/MainWindow.cs new file mode 100644 index 0000000..520eee1 --- /dev/null +++ b/mono/EllipseTruncate/MainWindow.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; + +namespace EllipseTruncate { + public class ActivePoint { + public Point point; + public ActivePoint(double x, double y) { + point = new Point(x, y); + } + } + + public class MainWindow : Gtk.Window { + static public void Main() { + Gtk.Application.Init(); + MainWindow win = new MainWindow(); + win.Show(); + win.Maximize(); + Gtk.Application.Run(); + } + + Point offset; + ActivePoint point; + List points = new List(); + + ActivePoint ellipse0 = new ActivePoint(500.0, 500.0), + ellipse1 = new ActivePoint(600.0, 500.0), + ellipse2 = new ActivePoint(500.0, 450.0); + ActivePoint bounds0 = new ActivePoint(450.0, 500.0), + bounds1 = new ActivePoint(500.0, 700.0), + bounds2 = new ActivePoint(600.0, 550.0); + + public MainWindow(): base(Gtk.WindowType.Toplevel) { + Events = Gdk.EventMask.KeyPressMask + | Gdk.EventMask.KeyReleaseMask + | Gdk.EventMask.ButtonPressMask + | Gdk.EventMask.ButtonReleaseMask + | Gdk.EventMask.ButtonMotionMask + | Gdk.EventMask.PointerMotionMask; + ExtensionEvents = Gdk.ExtensionMode.All; + points.AddRange(new ActivePoint[] {ellipse0, ellipse1, ellipse2, bounds0, bounds1, bounds2}); + } + + private bool refreshOnIdle() + { QueueDraw(); return false; } + private void Refresh() { + QueueDraw(); //GLib.Idle.Add(refreshOnIdle); + } + + protected override bool OnDeleteEvent(Gdk.Event e) { + Gtk.Application.Quit(); + return false; + } + + protected override bool OnExposeEvent(Gdk.EventExpose e) { + Cairo.Context context = Gdk.CairoHelper.Create(e.Window); + + context.Save(); + context.SetSourceRGBA(1.0, 1.0, 1.0, 1.0); + context.Rectangle(0, 0, Allocation.Width, Allocation.Height); + context.Fill(); + context.Restore(); + context.Save(); + context.Antialias = Cairo.Antialias.Gray; + + foreach(ActivePoint p in points) { + context.Save(); + context.SetSourceRGBA(0.0, point == p ? 1.0 : 0.0, 1.0, 1.0); + context.Arc(p.point.x, p.point.y, 5.0, 0, 2.0*Math.PI); + context.Fill(); + context.Restore(); + } + + { // draw bounds + Point p0 = bounds0.point, p1 = bounds1.point, p2 = bounds2.point; + context.Save(); + context.SetSourceRGBA(0.5, 0.5, 0.5, 0.2); + context.LineWidth = 2.0; + context.MoveTo(p0.x, p0.y); + context.LineTo(p1.x, p1.y); + context.LineTo(p1.x + p2.x - p0.x, p1.y + p2.y - p0.y); + context.LineTo(p2.x, p2.y); + context.ClosePath(); + context.Stroke(); + context.Restore(); + } + + // draw ellipse + Ellipse ellipse = new Ellipse(ellipse0.point, ellipse1.point, ellipse2.point); + ellipse.drawFull(context); + ellipse.drawTruncated(context, bounds0.point, bounds1.point, bounds2.point); + + context.Restore(); + context.Dispose(); + return false; + } + + protected override bool OnButtonPressEvent(Gdk.EventButton e) { + if (e.Button == 1) { + Point cursor = new Point(e.X, e.Y); + point = null; + foreach(ActivePoint p in points) + if ((p.point - cursor).len() <= 5.0) + { offset = p.point - cursor; point = p; } + } + Refresh(); + return false; + } + + protected override bool OnButtonReleaseEvent(Gdk.EventButton e) { + if (e.Button == 1) point = null; + Refresh(); + return false; + } + + protected override bool OnMotionNotifyEvent(Gdk.EventMotion e) { + if (point != null) { + Point cursor = new Point(e.X, e.Y); + point.point = cursor + offset; + } + Refresh(); + return false; + } + } +} diff --git a/mono/EllipseTruncate/Matrix.cs b/mono/EllipseTruncate/Matrix.cs new file mode 100644 index 0000000..272404f --- /dev/null +++ b/mono/EllipseTruncate/Matrix.cs @@ -0,0 +1,162 @@ +using System; + +namespace EllipseTruncate { + public struct Matrix { + public double m00, m01, m02, + m10, m11, m12, + m20, m21, m22; + + public Matrix(Point p0, Point p1, Point p2 = new Point()) { + m00 = p0.x; m01 = p0.y; m02 = 0.0; + m10 = p1.x; m11 = p1.y; m12 = 0.0; + m20 = p2.x; m21 = p2.y; m22 = 1.0; + } + + public Matrix(Cairo.Matrix m) { + m00 = m.Xx; m01 = m.Yx; m02 = 0.0; + m10 = m.Xy; m11 = m.Yy; m12 = 0.0; + m20 = m.X0; m21 = m.Y0; m22 = 1.0; + } + + public Point row0() + { return new Point(m00, m01); } + public Point row1() + { return new Point(m10, m11); } + public Point row2() + { return new Point(m20, m21); } + + public Point row(int index) { + switch(index) { + case 0: return row0(); + case 1: return row1(); + case 2: return row2(); + } + return new Point(); + } + + public static Matrix zero() + { return new Matrix(); } + + public static Matrix identity() { + Matrix m = new Matrix(); + m.m00 = m.m11 = m.m22 = 1.0; + return m; + } + + public static Matrix rotation(double angle) { + double s = Math.Sin(angle); + double c = Math.Cos(angle); + Matrix m = identity(); + m.m00 = c; m.m01 = s; + m.m10 =-s; m.m11 = c; + return m; + } + + public static Matrix translation(double x, double y) { + Matrix m = identity(); + m.m20 = x; m.m21 = y; + return m; + } + + public static Matrix scaling(double x, double y) { + Matrix m = identity(); + m.m00 = x; m.m11 = y; + return m; + } + + public static Matrix translation(Point t) + { return translation(t.x, t.y); } + public static Matrix scaling(Point s) + { return scaling(s.x, s.y); } + public static Matrix scaling(double s) + { return scaling(s, s); } + + public Matrix rotate(double angle) + { return this*rotation(angle); } + public Matrix translate(double x, double y) + { return this*translation(x, y); } + public Matrix translate(Point t) + { return this*translation(t); } + public Matrix scale(double x, double y) + { return this*scaling(x, y); } + public Matrix scale(Point s) + { return this*scaling(s); } + + public Point transform(Point p) { + return new Point( m00*p.x + m10*p.y + m20, + m01*p.x + m11*p.y + m21 ); + } + + public Point turn(Point p) { + return new Point( m00*p.x + m10*p.y, + m01*p.x + m11*p.y ); + } + + public static Point operator* (Matrix m, Point p) + { return m.transform(p); } + + public static Matrix operator* (Matrix a, Matrix b) { + Matrix m = new Matrix(); + + m.m00 = a.m00*b.m00 + a.m10*b.m01 + a.m20*b.m02; + m.m01 = a.m01*b.m00 + a.m11*b.m01 + a.m21*b.m02; + m.m02 = a.m02*b.m00 + a.m12*b.m01 + a.m22*b.m02; + + m.m10 = a.m00*b.m10 + a.m10*b.m11 + a.m20*b.m12; + m.m11 = a.m01*b.m10 + a.m11*b.m11 + a.m21*b.m12; + m.m12 = a.m02*b.m10 + a.m12*b.m11 + a.m22*b.m12; + + m.m20 = a.m00*b.m20 + a.m10*b.m21 + a.m20*b.m22; + m.m21 = a.m01*b.m20 + a.m11*b.m21 + a.m21*b.m22; + m.m22 = a.m02*b.m20 + a.m12*b.m21 + a.m22*b.m22; + + return m; + } + + public bool isEqual(Matrix other) { + return Math.Abs(m00 - other.m00) <= Geometry.precision + && Math.Abs(m01 - other.m01) <= Geometry.precision + && Math.Abs(m02 - other.m02) <= Geometry.precision + && Math.Abs(m10 - other.m10) <= Geometry.precision + && Math.Abs(m11 - other.m11) <= Geometry.precision + && Math.Abs(m12 - other.m12) <= Geometry.precision + && Math.Abs(m20 - other.m20) <= Geometry.precision + && Math.Abs(m21 - other.m21) <= Geometry.precision + && Math.Abs(m22 - other.m22) <= Geometry.precision; + } + + static double det2x2(double m00, double m01, double m10, double m11) + { return m00*m11 - m10*m01; } + + public double det() { + return m00*m11*m22 - m00*m12*m21 + + m01*m12*m20 - m01*m10*m22 + + m02*m10*m21 - m02*m11*m20; + } + + public Matrix invert() { + double d = det(); + Matrix m = new Matrix(); + if (Math.Abs(d) > Geometry.precision) { + d = 1.0/d; + double e = -d; + m.m00 = det2x2(m11, m12, m21, m22)*d; + m.m10 = det2x2(m10, m12, m20, m22)*e; + m.m20 = det2x2(m10, m11, m20, m21)*d; + + m.m01 = det2x2(m01, m02, m21, m22)*e; + m.m11 = det2x2(m00, m02, m20, m22)*d; + m.m21 = det2x2(m00, m01, m20, m21)*e; + + m.m02 = det2x2(m01, m02, m11, m12)*d; + m.m12 = det2x2(m00, m02, m10, m12)*e; + m.m22 = det2x2(m00, m01, m10, m11)*d; + } + return m; + } + + public Cairo.Matrix toCairo() + { return new Cairo.Matrix(m00, m01, m10, m11, m20, m21); } + } +} + diff --git a/mono/EllipseTruncate/Point.cs b/mono/EllipseTruncate/Point.cs new file mode 100644 index 0000000..9adb0e2 --- /dev/null +++ b/mono/EllipseTruncate/Point.cs @@ -0,0 +1,57 @@ +using System; + +namespace EllipseTruncate { + public struct Point { + public double x; + public double y; + + public Point(double x, double y) { + this.x = x; + this.y = y; + } + + public static Point operator+ (Point a, Point b) + { return new Point(a.x + b.x, a.y + b.y); } + public static Point operator- (Point a, Point b) + { return new Point(a.x - b.x, a.y - b.y); } + public static double operator* (Point b, Point a) + { return a.x*b.x + a.y*b.y; } + public static Point operator* (Point a, double b) + { return new Point(a.x*b, a.y*b); } + public static Point operator* (double b, Point a) + { return a*b; } + public static Point operator/ (Point a, double b) + { return new Point(a.x/b, a.y/b); } + + public bool isEqual(Point other) + { return (this - other).lenSqr() <= Geometry.precisionSqr; } + public double lenSqr() + { return x*x + y*y; } + public double len() + { return Math.Sqrt(lenSqr()); } + + public Point normalize() { + double l = len(); + return l > Geometry.precision ? this/l : this; + } + + public Point rotate90() + { return new Point(-y, x); } + + public Point rotate180() + { return new Point(-x, -y); } + + public Point rotate270() + { return new Point(y, -x); } + + public Point rotate(double angle) { + double s = Math.Sin(angle); + double c = Math.Cos(angle); + return new Point(c*x - s*y, s*x + c*y); + } + + public double atan() + { return Math.Atan2(y, x); } + } +} + diff --git a/mono/EllipseTruncate/Rectangle.cs b/mono/EllipseTruncate/Rectangle.cs new file mode 100644 index 0000000..372eaf4 --- /dev/null +++ b/mono/EllipseTruncate/Rectangle.cs @@ -0,0 +1,85 @@ +using System; + +namespace EllipseTruncate { + public struct Rectangle { + public double x0, y0, x1, y1; + + public Point p0 { + get { return new Point(x0, y0); } + set { x0 = value.x; y0 = value.y; } + } + + public Point p1 { + get { return new Point(x1, y1); } + set { x1 = value.x; y1 = value.y; } + } + + public double width { + get { return x1 - x0; } + set { x1 = x0 + value; } + } + + public double height { + get { return y1 - y0; } + set { y1 = y0 + value; } + } + + public bool empty { + get { return width <= Geometry.precision || height <= Geometry.precision; } + } + + public Rectangle(double x0, double y0, double x1, double y1) { + this.x0 = x0; + this.y0 = y0; + this.x1 = x1; + this.y1 = y1; + } + public Rectangle(double x, double y): + this(x, y, x, y) { } + public Rectangle(Point p): + this(p.x, p.y) { } + public Rectangle(Point p0, Point p1): + this(p0.x, p0.y, p1.x, p1.y) { } + + public Rectangle expand(Point p, double radius = 0.0) { + return new Rectangle( + Math.Min(x0, p.x), + Math.Min(y0, p.y), + Math.Max(x1, p.x), + Math.Max(y1, p.y) ); + } + + public Rectangle inflate(double x, double y) { + return new Rectangle( + x0 - x, + y0 - y, + x1 + x, + y1 + y ); + } + + public Rectangle inflate(double size) { + return inflate(size, size); + } + + public static Rectangle operator| (Rectangle a, Rectangle b) { + Rectangle rect = a.empty ? b + : b.empty ? a + : new Rectangle( Math.Min(a.x0, b.x0), + Math.Min(a.y0, b.y0), + Math.Max(a.x1, b.x1), + Math.Max(a.y1, b.y1) ); + return rect.empty ? new Rectangle() : rect; + } + + public static Rectangle operator& (Rectangle a, Rectangle b) { + Rectangle rect = a.empty ? b + : b.empty ? a + : new Rectangle( Math.Max(a.x0, b.x0), + Math.Max(a.y0, b.y0), + Math.Min(a.x1, b.x1), + Math.Min(a.y1, b.y1) ); + return rect.empty ? new Rectangle() : rect; + } + } +} +