/// <summary> /// Compute edge intersections with bounding box. /// </summary> private void PostProcess(Rectangle box) { foreach (var edge in rays) { // The vertices of the infinite edge. var v1 = (Point)edge.origin; var v2 = (Point)edge.twin.origin; if (box.Contains(v1) || box.Contains(v2)) { // Move infinite vertex v2 onto the box boundary. IntersectionHelper.BoxRayIntersection(box, v1, v2, ref v2); } else { // There is actually no easy way to handle the second case. The two edges // leaving v1, pointing towards the mesh, don't have to intersect the box // (the could join with edges of other cells outside the box). // A general intersection algorithm (DCEL <-> Rectangle) is needed, which // computes intersections with all edges and discards objects outside the // box. } } }
public StandardVoronoi(Mesh mesh, Rectangle box, IVoronoiFactory factory, IPredicates predicates) : base(mesh, factory, predicates, true) { // We assume the box to be at least as large as the mesh. box.Expand(mesh.bounds); // We explicitly told the base constructor to call the Generate method, so // at this point the basic Voronoi diagram is already created. PostProcess(box); }
/// <summary> /// Intersect segment with a bounding box. /// </summary> /// <param name="rect">The clip rectangle.</param> /// <param name="p0">Segment endpoint.</param> /// <param name="p1">Segment endpoint.</param> /// <param name="c0">The new location of p0.</param> /// <param name="c1">The new location of p1.</param> /// <returns>Returns true, if segment is clipped.</returns> /// <remarks> /// Based on Liang-Barsky function by Daniel White: /// http://www.skytopia.com/project/articles/compsci/clipping.html /// </remarks> public static bool LiangBarsky(Rectangle rect, Point p0, Point p1, ref Point c0, ref Point c1) { // Define the x/y clipping values for the border. double xmin = rect.Left; double xmax = rect.Right; double ymin = rect.Bottom; double ymax = rect.Top; // Define the start and end points of the line. double x0 = p0.X; double y0 = p0.Y; double x1 = p1.X; double y1 = p1.Y; double t0 = 0.0; double t1 = 1.0; double dx = x1 - x0; double dy = y1 - y0; double p = 0.0, q = 0.0, r; for (int edge = 0; edge < 4; edge++) { // Traverse through left, right, bottom, top edges. if (edge == 0) { p = -dx; q = -(xmin - x0); } if (edge == 1) { p = dx; q = (xmax - x0); } if (edge == 2) { p = -dy; q = -(ymin - y0); } if (edge == 3) { p = dy; q = (ymax - y0); } r = q / p; if (p == 0 && q < 0) return false; // Don't draw line at all. (parallel line outside) if (p < 0) { if (r > t1) return false; // Don't draw line at all. else if (r > t0) t0 = r; // Line is clipped! } else if (p > 0) { if (r < t0) return false; // Don't draw line at all. else if (r < t1) t1 = r; // Line is clipped! } } c0.X = x0 + t0 * dx; c0.Y = y0 + t0 * dy; c1.X = x0 + t1 * dx; c1.Y = y0 + t1 * dy; return true; // (clipped) line is drawn }
public static Rectangle Bounds(this ITriangle triangle) { var bounds = new Rectangle(); for (int i = 0; i < 3; i++) { bounds.Expand(triangle.GetVertex(i)); } return bounds; }
/// <inheritdoc /> public Rectangle Bounds() { var bounds = new Rectangle(); bounds.Expand(this.points.ToPoints()); return bounds; }
private void UpdateMetrics(Rectangle bounds) { x_max = bounds.Right; x_min = bounds.Left; y_max = bounds.Top; y_min = bounds.Bottom; // Enlarge width 5% on each side double x_scale = x_max - x_min; x_max = x_max + 0.05 * x_scale; x_min = x_min - 0.05 * x_scale; x_scale = x_max - x_min; // Enlarge height 5% on each side double y_scale = y_max - y_min; y_max = y_max + 0.05 * y_scale; y_min = y_min - 0.05 * y_scale; y_scale = y_max - y_min; if (x_scale < y_scale) { int delta = (int)Math.Round((ps.Right - ps.X) * (y_scale - x_scale) / (2.0 * y_scale)); ps.Expand(-delta, 0); clip.Expand(-delta, 0); } else { int delta = (int)Math.Round((ps.Bottom - ps.Y) * (x_scale - y_scale) / (2.0 * x_scale)); ps.Expand(0, -delta); clip.Expand(0, -delta); } }
protected List<Vertex> CreateRectangle(Rectangle rect, int nH, int nV, int boundary = 0) { var contour = new List<Vertex>(2 * nH + 2 * nV); // Horizontal and vertical step sizes. double stepH = rect.Width / nH; double stepV = rect.Height / nV; // Left box boundary points for (int i = 0; i < nV; i++) { contour.Add(new Vertex(rect.Left, rect.Bottom + i * stepV, 1)); } // Top box boundary points for (int i = 0; i < nH; i++) { contour.Add(new Vertex(rect.Left + i * stepH, rect.Top, 1)); } // Right box boundary points for (int i = 0; i < nV; i++) { contour.Add(new Vertex(rect.Right, rect.Top - i * stepV, 1)); } // Bottom box boundary points for (int i = 0; i < nH; i++) { contour.Add(new Vertex(rect.Right - i * stepH, rect.Bottom, 1)); } return contour; }
/// <summary> /// Read the vertices from memory. /// </summary> /// <param name="data">The input data.</param> internal void TransferNodes(IList<Vertex> points) { this.invertices = points.Count; this.mesh_dim = 2; this.bounds = new Rectangle(); if (this.invertices < 3) { logger.Error("Input must have at least three input vertices.", "Mesh.TransferNodes()"); throw new Exception("Input must have at least three input vertices."); } var v = points[0]; #if USE_ATTRIBS // Check attributes. this.nextras = v.attributes == null ? 0 : v.attributes.Length; #endif // Simple heuristic to check if ids are already set. We assume that if the // first two vertex ids are distinct, then all input vertices have pairwise // distinct ids. bool userId = (v.id != points[1].id); foreach (var p in points) { if (userId) { p.hash = p.id; // Make sure the hash counter gets updated. hash_vtx = Math.Max(p.hash + 1, hash_vtx); } else { p.hash = p.id = hash_vtx++; } this.vertices.Add(p.hash, p); this.bounds.Expand(p); } }
public QuadNode(Rectangle box, TriangleQuadTree tree, bool init) { this.tree = tree; this.bounds = new Rectangle(box.Left, box.Bottom, box.Width, box.Height); this.pivot = new Point((box.Left + box.Right) / 2, (box.Bottom + box.Top) / 2); this.bitRegions = 0; this.regions = new QuadNode[4]; this.triangles = new List<int>(); if (init) { int count = tree.triangles.Length; // Allocate memory upfront triangles.Capacity = count; for (int i = 0; i < count; i++) { triangles.Add(i); } } }
/// <summary> /// Expand rectangle to include given rectangle. /// </summary> /// <param name="x">X coordinate.</param> /// <param name="y">Y coordinate.</param> public void Expand(Rectangle other) { xmin = Math.Min(xmin, other.xmin); ymin = Math.Min(ymin, other.ymin); xmax = Math.Max(xmax, other.xmax); ymax = Math.Max(ymax, other.ymax); }
private static Point FindPointInPolygon(List<Vertex> contour, int limit, double eps) { var bounds = new Rectangle(); bounds.Expand(contour.ToPoints()); int length = contour.Count; var test = new Point(); Point a, b, c; // Current corner points. double bx, by; double dx, dy; double h; var predicates = new RobustPredicates(); a = contour[0]; b = contour[1]; for (int i = 0; i < length; i++) { c = contour[(i + 2) % length]; // Corner point. bx = b.x; by = b.y; // NOTE: if we knew the contour points were in counterclockwise order, we // could skip concave corners and search only in one direction. h = predicates.CounterClockwise(a, b, c); if (Math.Abs(h) < eps) { // Points are nearly co-linear. Use perpendicular direction. dx = (c.y - a.y) / 2; dy = (a.x - c.x) / 2; } else { // Direction [midpoint(a-c) -> corner point] dx = (a.x + c.x) / 2 - bx; dy = (a.y + c.y) / 2 - by; } // Move around the contour. a = b; b = c; h = 1.0; for (int j = 0; j < limit; j++) { // Search in direction. test.x = bx + dx * h; test.y = by + dy * h; if (bounds.Contains(test) && IsPointInPolygon(test, contour)) { return test; } // Search in opposite direction (see NOTE above). test.x = bx - dx * h; test.y = by - dy * h; if (bounds.Contains(test) && IsPointInPolygon(test, contour)) { return test; } h = h / 2; } } throw new Exception(); }
/// <summary> /// Intersect a ray with a bounding box. /// </summary> /// <param name="rect">The clip rectangle.</param> /// <param name="p0">The ray startpoint (inside the box).</param> /// <param name="p1">Any point in ray direction (NOT the direction vector).</param> /// <param name="c1">The intersection point.</param> /// <returns>Returns false, if startpoint is outside the box.</returns> public static bool BoxRayIntersection(Rectangle rect, Point p0, Point p1, ref Point c1) { double x = p0.X; double y = p0.Y; double dx = p1.x - x; double dy = p1.y - y; double t1, x1, y1, t2, x2, y2; // Bounding box double xmin = rect.Left; double xmax = rect.Right; double ymin = rect.Bottom; double ymax = rect.Top; // Check if point is inside the bounds if (x < xmin || x > xmax || y < ymin || y > ymax) { return false; } // Calculate the cut through the vertical boundaries if (dx < 0) { // Line going to the left: intersect with x = minX t1 = (xmin - x) / dx; x1 = xmin; y1 = y + t1 * dy; } else if (dx > 0) { // Line going to the right: intersect with x = maxX t1 = (xmax - x) / dx; x1 = xmax; y1 = y + t1 * dy; } else { // Line going straight up or down: no intersection possible t1 = double.MaxValue; x1 = y1 = 0; } // Calculate the cut through upper and lower boundaries if (dy < 0) { // Line going downwards: intersect with y = minY t2 = (ymin - y) / dy; x2 = x + t2 * dx; y2 = ymin; } else if (dy > 0) { // Line going upwards: intersect with y = maxY t2 = (ymax - y) / dy; x2 = x + t2 * dx; y2 = ymax; } else { // Horizontal line: no intersection possible t2 = double.MaxValue; x2 = y2 = 0; } if (t1 < t2) { c1.x = x1; c1.y = y1; } else { c1.x = x2; c1.y = y2; } return true; }
public static TriangleNet.Geometry.Point FindPointInPolygon(SectionContour contour, List <SectionContour> otherContours, int limit, double eps = 2e-5) { List <Vertex> poly = contour.Points.Select(p => new Vertex(p.X, p.Y)).ToList(); var bounds = new TriangleNet.Geometry.Rectangle(); bounds.Expand(poly); int length = poly.Count; var test = new TriangleNet.Geometry.Point(); TriangleNet.Geometry.Point a, b, c; // Current corner points. double bx, by; double dx, dy; double h; var predicates = new RobustPredicates(); a = poly[0]; b = poly[1]; for (int i = 0; i < length; i++) { c = poly[(i + 2) % length]; // Corner point. bx = b.X; by = b.Y; // NOTE: if we knew the contour points were in counterclockwise order, we // could skip concave corners and search only in one direction. h = predicates.CounterClockwise(a, b, c); if (Math.Abs(h) < eps) { // Points are nearly co-linear. Use perpendicular direction. dx = (c.Y - a.Y) / 2; dy = (a.X - c.X) / 2; } else { // Direction [midpoint(a-c) -> corner point] dx = (a.X + c.X) / 2 - bx; dy = (a.Y + c.Y) / 2 - by; } // Move around the contour. a = b; b = c; h = 1.0; for (int j = 0; j < limit; j++) { // Search in direction. test.X = bx + dx * h; test.Y = by + dy * h; if (bounds.Contains(test) && IsPointInPolygon(test, contour, otherContours)) { return(test); } // Search in opposite direction (see NOTE above). test.X = bx - dx * h; test.Y = by - dy * h; if (bounds.Contains(test) && IsPointInPolygon(test, contour, otherContours)) { return(test); } h = h / 2; } } throw new Exception(); }
private static Point FindPointInPolygon(List<Vertex> contour) { var bounds = new Rectangle(); bounds.Expand(contour); int length = contour.Count; int limit = 8; var test = new Point(); Point a, b; // Current edge. double cx, cy; // Center of current edge. double dx, dy; // Direction perpendicular to edge. for (int i = 0; i < length; i++) { a = contour[i]; b = contour[(i + 1) % length]; cx = (a.x + b.x) / 2; cy = (a.y + b.y) / 2; dx = (b.y - a.y) / 1.374; dy = (a.x - b.x) / 1.374; for (int j = 1; j <= limit; j++) { // Search to the right of the segment. test.x = cx + dx / j; test.y = cy + dy / j; if (bounds.Contains(test) && IsPointInPolygon(test, contour)) { return test; } // Search on the other side of the segment. test.x = cx - dx / j; test.y = cy - dy / j; if (bounds.Contains(test) && IsPointInPolygon(test, contour)) { return test; } } } throw new Exception(); }
/// <summary> /// Check if given rectangle is inside bounding box. /// </summary> /// <param name="other">Rectangle to check.</param> /// <returns>Return true, if bounding box contains given rectangle.</returns> public bool Contains(Rectangle other) { return (xmin <= other.Left && other.Right <= xmax && ymin <= other.Bottom && other.Top <= ymax); }
public QuadNode(Rectangle box, TriangleQuadTree tree) : this(box, tree, false) { }
/// <summary> /// Check if given rectangle intersects bounding box. /// </summary> /// <param name="other">Rectangle to check.</param> /// <returns>Return true, if given rectangle intersects bounding box.</returns> public bool Intersects(Rectangle other) { return (other.Left < xmax && xmin < other.Right && other.Bottom < ymax && ymin < other.Top); }
public void CreateSubRegion(int currentDepth) { // The four sub regions of the quad tree // +--------------+ // | nw 2 | ne 3 | // |------+pivot--| // | sw 0 | se 1 | // +--------------+ Rectangle box; var width = bounds.Right - pivot.x; var height = bounds.Top - pivot.y; // 1. region south west box = new Rectangle(bounds.Left, bounds.Bottom, width, height); regions[0] = new QuadNode(box, tree); // 2. region south east box = new Rectangle(pivot.x, bounds.Bottom, width, height); regions[1] = new QuadNode(box, tree); // 3. region north west box = new Rectangle(bounds.Left, pivot.y, width, height); regions[2] = new QuadNode(box, tree); // 4. region north east box = new Rectangle(pivot.x, pivot.y, width, height); regions[3] = new QuadNode(box, tree); Point[] triangle = new Point[3]; // Find region for every triangle vertex foreach (var index in triangles) { ITriangle tri = tree.triangles[index]; triangle[0] = tri.GetVertex(0); triangle[1] = tri.GetVertex(1); triangle[2] = tri.GetVertex(2); AddTriangleToRegion(triangle, index); } for (int i = 0; i < 4; i++) { if (regions[i].triangles.Count > tree.sizeBound && currentDepth < tree.maxDepth) { regions[i].CreateSubRegion(currentDepth + 1); } } }
public Rectangle(Rectangle other) : this(other.Left, other.Bottom, other.Right, other.Top) { }
protected List<Vertex> CreateRectangle(Rectangle rect, int n, int boundary = 0) { return CreateRectangle(rect, n, n, boundary); }
/// <summary> /// Gets the Voronoi diagram as raw output data. /// </summary> /// <param name="mesh"></param> /// <returns></returns> /// <remarks> /// The Voronoi diagram is the geometric dual of the Delaunay triangulation. /// Hence, the Voronoi vertices are listed by traversing the Delaunay /// triangles, and the Voronoi edges are listed by traversing the Delaunay /// edges. ///</remarks> private void Generate() { mesh.Renumber(); mesh.MakeVertexMap(); // Allocate space for voronoi diagram this.points = new Point[mesh.triangles.Count + mesh.hullsize]; this.regions = new Dictionary<int, VoronoiRegion>(mesh.vertices.Count); rayPoints = new Dictionary<int, Point>(); rayIndex = 0; bounds = new Rectangle(); // Compute triangles circumcenters and setup bounding box ComputeCircumCenters(); // Add all Voronoi regions to the map. foreach (var vertex in mesh.vertices.Values) { regions.Add(vertex.id, new VoronoiRegion(vertex)); } // Loop over the mesh vertices (Voronoi generators). foreach (var region in regions.Values) { //if (item.Boundary == 0) { ConstructCell(region); } } }
/// <summary> /// Generates a structured mesh. /// </summary> /// <param name="bounds">Bounds of the mesh.</param> /// <param name="nx">Number of segments in x direction.</param> /// <param name="ny">Number of segments in y direction.</param> /// <returns>Mesh</returns> public static IMesh StructuredMesh(Rectangle bounds, int nx, int ny) { var polygon = new Polygon((nx + 1) * (ny + 1)); double x, y, dx, dy, left, bottom; dx = bounds.Width / nx; dy = bounds.Height / ny; left = bounds.Left; bottom = bounds.Bottom; int i, j, k, l, n = 0; // Add vertices. var points = new Vertex[(nx + 1) * (ny + 1)]; for (i = 0; i <= nx; i++) { x = left + i * dx; for (j = 0; j <= ny; j++) { y = bottom + j * dy; points[n++] = new Vertex(x, y); } } polygon.Points.AddRange(points); n = 0; // Set vertex hash and id. foreach (var v in points) { v.hash = v.id = n++; } // Add boundary segments. var segments = polygon.Segments; segments.Capacity = 2 * (nx + ny); Vertex a, b; for (j = 0; j < ny; j++) { // Left a = points[j]; b = points[j + 1]; segments.Add(new Segment(a, b, 1)); a.Label = b.Label = 1; // Right a = points[nx * (ny + 1) + j]; b = points[nx * (ny + 1) + (j + 1)]; segments.Add(new Segment(a, b, 1)); a.Label = b.Label = 1; } for (i = 0; i < nx; i++) { // Bottom a = points[(ny + 1) * i]; b = points[(ny + 1) * (i + 1)]; segments.Add(new Segment(a, b, 1)); a.Label = b.Label = 1; // Top a = points[ny + (ny + 1) * i]; b = points[ny + (ny + 1) * (i + 1)]; segments.Add(new Segment(a, b, 1)); a.Label = b.Label = 1; } // Add triangles. var triangles = new InputTriangle[2 * nx * ny]; n = 0; for (i = 0; i < nx; i++) { for (j = 0; j < ny; j++) { k = j + (ny + 1) * i; l = j + (ny + 1) * (i + 1); // Create 2 triangles in rectangle [k, l, l + 1, k + 1]. if ((i + j) % 2 == 0) { // Diagonal from bottom left to top right. triangles[n++] = new InputTriangle(k, l, l + 1); triangles[n++] = new InputTriangle(k, l + 1, k + 1); } else { // Diagonal from top left to bottom right. triangles[n++] = new InputTriangle(k, l, k + 1); triangles[n++] = new InputTriangle(l, l + 1, k + 1); } } } return Converter.ToMesh(polygon, triangles); }
public StandardVoronoi(Mesh mesh, Rectangle box) : this(mesh, box, new DefaultVoronoiFactory(), RobustPredicates.Default) { }