1 | #region License Information
|
---|
2 | /* HeuristicLab
|
---|
3 | * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
|
---|
4 | *
|
---|
5 | * This file is part of HeuristicLab.
|
---|
6 | *
|
---|
7 | * HeuristicLab is free software: you can redistribute it and/or modify
|
---|
8 | * it under the terms of the GNU General Public License as published by
|
---|
9 | * the Free Software Foundation, either version 3 of the License, or
|
---|
10 | * (at your option) any later version.
|
---|
11 | *
|
---|
12 | * HeuristicLab is distributed in the hope that it will be useful,
|
---|
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
15 | * GNU General Public License for more details.
|
---|
16 | *
|
---|
17 | * You should have received a copy of the GNU General Public License
|
---|
18 | * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
|
---|
19 | */
|
---|
20 | #endregion
|
---|
21 |
|
---|
22 | using System;
|
---|
23 | using System.Collections.Generic;
|
---|
24 |
|
---|
25 | namespace HeuristicLab.Visualization {
|
---|
26 | public static class PrimitiveUtil {
|
---|
27 | public static List<PointD> ComputeIntersect(this Rectangle rect, Line line) {
|
---|
28 | var a = rect.LowerLeft;
|
---|
29 | var c = rect.UpperRight;
|
---|
30 | var b = new PointD(c.X, a.Y);
|
---|
31 | var d = new PointD(a.X, c.Y);
|
---|
32 | var ls = line.Start;
|
---|
33 | var le = line.End;
|
---|
34 |
|
---|
35 | var intersectionPoints = new List<PointD>();
|
---|
36 | var p = PointD.Empty;
|
---|
37 | if (LineIntersect(ls, le, a, b, ref p)) intersectionPoints.Add(p);
|
---|
38 | if (LineIntersect(ls, le, b, c, ref p)) intersectionPoints.Add(p);
|
---|
39 | if (LineIntersect(ls, le, c, d, ref p)) intersectionPoints.Add(p);
|
---|
40 | if (LineIntersect(ls, le, d, a, ref p)) intersectionPoints.Add(p);
|
---|
41 |
|
---|
42 | // return the intersection points in the order of the distance to the start of the line
|
---|
43 | intersectionPoints.Sort((p1, p2) => EuclideanDistance(p1, line.Start).CompareTo(EuclideanDistance(p2, line.Start)));
|
---|
44 |
|
---|
45 | return intersectionPoints;
|
---|
46 | }
|
---|
47 |
|
---|
48 | public static List<PointD> ComputeIntersect(this Ellipse ell, Line line) {
|
---|
49 | var intersectionPoints = new List<PointD>();
|
---|
50 |
|
---|
51 | var x1 = line.Start.X;
|
---|
52 | var y1 = line.Start.Y;
|
---|
53 | var x2 = line.End.X;
|
---|
54 | var y2 = line.End.Y;
|
---|
55 | var midX = ell.Center().X;
|
---|
56 | var midY = ell.Center().Y;
|
---|
57 | var h = ell.Size.Width / 2;
|
---|
58 | var v = ell.Size.Height / 2;
|
---|
59 |
|
---|
60 | // translate coordinate system to be the center of the ellipse
|
---|
61 | x1 -= midX;
|
---|
62 | y1 -= midY;
|
---|
63 |
|
---|
64 | x2 -= midX;
|
---|
65 | y2 -= midY;
|
---|
66 |
|
---|
67 | if (x1.IsAlmost(x2)) {
|
---|
68 | var y = (v / h) * Math.Sqrt(h * h - x1 * x1);
|
---|
69 | if (Math.Min(y1, y2) <= y && y <= Math.Max(y1, y2)) {
|
---|
70 | intersectionPoints.Add(new PointD(x1 + midX, y + midY));
|
---|
71 | }
|
---|
72 | if (Math.Min(y1, y2) <= -y && -y <= Math.Max(y1, y2)) {
|
---|
73 | intersectionPoints.Add(new PointD(x1 + midX, -y + midY));
|
---|
74 | }
|
---|
75 | return intersectionPoints;
|
---|
76 | }
|
---|
77 |
|
---|
78 | var a = (y2 - y1) / (x2 - x1);
|
---|
79 | var b = y1 - a * x1;
|
---|
80 | var h2 = h * h;
|
---|
81 | var v2 = v * v;
|
---|
82 | var r = a * a * h2 + v2;
|
---|
83 | var s = 2 * a * b * h2;
|
---|
84 | var t = h2 * (b * b - v2);
|
---|
85 | var d = s * s - 4 * r * t;
|
---|
86 |
|
---|
87 | if (d > 0) {
|
---|
88 | var xi1 = (-s + Math.Sqrt(d)) / (2 * r);
|
---|
89 | var xi2 = (-s - Math.Sqrt(d)) / (2 * r);
|
---|
90 |
|
---|
91 | var yi1 = a * xi1 + b;
|
---|
92 | var yi2 = a * xi2 + b;
|
---|
93 |
|
---|
94 | if (isPointInLine(x1, x2, y1, y2, xi1, yi1)) {
|
---|
95 | intersectionPoints.Add(new PointD(xi1 + midX, yi1 + midY));
|
---|
96 | }
|
---|
97 | if (isPointInLine(x1, x2, y1, y2, xi2, yi2)) {
|
---|
98 | intersectionPoints.Add(new PointD(xi2 + midX, yi2 + midY));
|
---|
99 | }
|
---|
100 | } else if (d.IsAlmost(0)) {
|
---|
101 | var xi = -s / (2 * r);
|
---|
102 | var yi = a * xi + b;
|
---|
103 |
|
---|
104 | if (isPointInLine(x1, x2, y1, y2, xi, yi)) {
|
---|
105 | intersectionPoints.Add(new PointD(xi + midX, yi + midY));
|
---|
106 | }
|
---|
107 | }
|
---|
108 | intersectionPoints.Sort((p1, p2) => EuclideanDistance(p1, line.Start).CompareTo(EuclideanDistance(p2, line.Start)));
|
---|
109 | return intersectionPoints;
|
---|
110 | }
|
---|
111 |
|
---|
112 | public static PointD Center(this RectangularPrimitiveBase primitive) {
|
---|
113 | return new PointD((primitive.LowerLeft.X + primitive.UpperRight.X) / 2, (primitive.LowerLeft.Y + primitive.UpperRight.Y) / 2);
|
---|
114 | }
|
---|
115 |
|
---|
116 | #region lowlevel
|
---|
117 | public static double EuclideanDistance(PointD a, PointD b) {
|
---|
118 | var x = a.X - b.X;
|
---|
119 | var y = a.Y - b.Y;
|
---|
120 | return Math.Sqrt(x * x + y * y);
|
---|
121 | }
|
---|
122 |
|
---|
123 | public static bool isPointInLine(double x1, double x2, double y1, double y2, double px, double py) {
|
---|
124 | double xMin = Math.Min(x1, x2);
|
---|
125 | double xMax = Math.Max(x1, x2);
|
---|
126 |
|
---|
127 | double yMin = Math.Min(y1, y2);
|
---|
128 | double yMax = Math.Max(y1, y2);
|
---|
129 |
|
---|
130 | return xMin <= px && px <= xMax && yMin <= py && py <= yMax;
|
---|
131 | }
|
---|
132 |
|
---|
133 | private static bool IsAlmost(this double x, double y, double eps = 1e-12) {
|
---|
134 | return Math.Abs(x - y) < eps;
|
---|
135 | }
|
---|
136 | // finds the intersection point between line segments AB and CD
|
---|
137 | private static bool LineIntersect(PointD a, PointD b, PointD c, PointD d, ref PointD intersectionPoint) {
|
---|
138 | // false if either segment is zero-length
|
---|
139 | if (a == b || c == d)
|
---|
140 | return false;
|
---|
141 |
|
---|
142 | // false if the segments share a common endpoint
|
---|
143 | if (a == c || b == c || a == d || b == d)
|
---|
144 | return false;
|
---|
145 |
|
---|
146 | // translate the reference system so that point A is the new origin
|
---|
147 | var bx = b.X - a.X;
|
---|
148 | var by = b.Y - a.Y;
|
---|
149 | var cx = c.X - a.X;
|
---|
150 | var cy = c.Y - a.Y;
|
---|
151 | var dx = d.X - a.X;
|
---|
152 | var dy = d.Y - a.Y;
|
---|
153 |
|
---|
154 | var ab = Math.Sqrt(bx * bx + by * by);
|
---|
155 |
|
---|
156 | var sin = by / ab;
|
---|
157 | var cos = bx / ab;
|
---|
158 |
|
---|
159 | // rotate the system so that point B is on the positive X axis
|
---|
160 | var tmp = cx * cos + cy * sin;
|
---|
161 | cy = cy * cos - cx * sin;
|
---|
162 | cx = tmp;
|
---|
163 |
|
---|
164 | tmp = dx * cos + dy * sin;
|
---|
165 | dy = dy * cos - dx * sin;
|
---|
166 | dx = tmp;
|
---|
167 |
|
---|
168 | // return false if CD doesn't cross AB
|
---|
169 | if (cy < 0 && dy < 0 || cy >= 0 && dy >= 0)
|
---|
170 | return false;
|
---|
171 |
|
---|
172 | var xIntersect = dx + (cx - dx) * dy / (dy - cy);
|
---|
173 |
|
---|
174 | if (xIntersect < 0 || xIntersect > ab)
|
---|
175 | return false;
|
---|
176 |
|
---|
177 | intersectionPoint = new PointD(a.X + xIntersect * cos, a.Y + xIntersect * sin);
|
---|
178 | return true;
|
---|
179 | }
|
---|
180 | #endregion
|
---|
181 | }
|
---|
182 | }
|
---|