public static Ngons MinkowskiSum(Ngon pattern, Ngons subject, NFPQUALITY quality, bool flip_pattern) { switch (quality) { case NFPQUALITY.Simple: return(MSumSimple(pattern, subject, flip_pattern)); case NFPQUALITY.Convex: return(MSumConvex(pattern, subject, flip_pattern)); case NFPQUALITY.ConcaveLight: return(MSumConcave(pattern, subject, flip_pattern, 0.25)); case NFPQUALITY.ConcaveMedium: return(MSumConcave(pattern, subject, flip_pattern, 0.55)); case NFPQUALITY.ConcaveHigh: return(MSumConcave(pattern, subject, flip_pattern, 0.85)); case NFPQUALITY.ConcaveFull: return(MSumConcave(pattern, subject, flip_pattern, 1.0)); case NFPQUALITY.Full: return(MSumFull(pattern, subject, flip_pattern)); default: return(null); } }
private static Ngons MSumSimple(Ngon pattern, Ngons subject, bool flip_pattern) { IntRect pB = GetBounds(pattern); IntRect sB = GetBounds(subject[0]); if (flip_pattern) { pB = new IntRect(-pB.right, -pB.bottom, -pB.left, -pB.top); } long l = pB.left + sB.left; long r = pB.right + sB.right; long t = pB.top + sB.top; long b = pB.bottom + sB.bottom; Ngon p = new Ngon() { new IntPoint(l, b), new IntPoint(r, b), new IntPoint(r, t), new IntPoint(l, t) }; return(new Ngons() { p }); }
private static Ngons MSumFull(Ngon pattern, Ngons subject, bool flip_pattern) { Clipper clipper = new Clipper(); Ngons full = new Ngons(); long scale = flip_pattern ? -1 : 1; for (int i = 0; i < pattern.Count; i++) { clipper.AddPaths(subject.Clone(scale * pattern[i].X, scale * pattern[i].Y), PolyType.ptSubject, true); } clipper.Execute(ClipType.ctUnion, full, PolyFillType.pftNonZero); clipper.Clear(); clipper.AddPaths(full, PolyType.ptSubject, true); clipper.AddPaths(MinkowskiSumBoundary(pattern, subject, flip_pattern), PolyType.ptSubject, true); Ngons res = new Ngons(); clipper.Execute(ClipType.ctUnion, res, PolyFillType.pftNonZero); return(res); }
public static Ngons MinkowskiSumSegment(Ngon pattern, IntPoint p1, IntPoint p2, bool flip_pattern) { Clipper clipper = new Clipper(); Ngon p1_c = pattern.Clone(p1.X, p1.Y, flip_pattern); if (p1 == p2) { return new Ngons() { p1_c } } ; Ngon p2_c = pattern.Clone(p2.X, p2.Y, flip_pattern); Ngons full = new Ngons(); clipper.AddPath(p1_c, PolyType.ptSubject, true); clipper.AddPath(p2_c, PolyType.ptSubject, true); clipper.AddPaths(Clipper.MinkowskiSum(pattern.Clone(0, 0, flip_pattern), new Ngon() { p1, p2 }, false), PolyType.ptSubject, true); clipper.Execute(ClipType.ctUnion, full, PolyFillType.pftNonZero); return(full); }
public static Ngons Clone(this Ngons polys, Mat3x3 T) { Ngons clone = new Ngons(polys.Count); for (int i = 0; i < polys.Count; i++) { clone.Add(polys[i].Clone(T)); } return(clone); }
public static Ngons Clone(this Ngons polys, long shift_x, long shift_y, bool flip_first = false) { Ngons clone = new Ngons(polys.Count); for (int i = 0; i < polys.Count; i++) { clone.Add(polys[i].Clone(shift_x, shift_y, flip_first)); } return(clone); }
public Ngons GetTransformedPoly() { Ngons n = new Ngons(poly.Count); for (int i = 0; i < poly.Count; i++) { Ngon nn = new Ngon(poly[i].Count); for (int j = 0; j < poly[i].Count; j++) { nn.Add(GetTransformedPoint(i, j)); } n.Add(nn); } return(n); }
private static Ngons MSumConcave(Ngon pattern, Ngons subject, bool flip_pattern, double rigidness = 1.0) { Ngon subj = subject[0]; Ngon patt = pattern.Clone(0, 0, flip_pattern); if (rigidness < 1.0) { subj = ConvexHull(subj, rigidness); patt = ConvexHull(patt, rigidness); } Ngons sres = MinkowskiSumBoundary(patt, subj, false); return(sres.Count == 0 ? sres : new Ngons() { sres[0] }); }
public static Ngons MinkowskiSumBoundary(Ngon pattern, Ngons path, bool flip_pattern) { Clipper clipper = new Clipper(); Ngons full = new Ngons(); for (int i = 0; i < path.Count; i++) { Ngons seg = MinkowskiSumBoundary(pattern, path[i], flip_pattern); clipper.AddPaths(full, PolyType.ptSubject, true); clipper.AddPaths(seg, PolyType.ptSubject, true); Ngons res = new Ngons(); clipper.Execute(ClipType.ctUnion, res, PolyFillType.pftNonZero); full = res; clipper.Clear(); } return(full); }
public int AddMinkowskiSum(int subj_handle, int pattern_handle, NFPQUALITY quality, bool flip_pattern, int set_at = -1) { Ngons A = polygon_lib[subj_handle].GetTransformedPoly(); Ngons B = polygon_lib[pattern_handle].GetTransformedPoly(); Ngons C = GeomUtility.MinkowskiSum(B[0], A, quality, flip_pattern); PolyRef pref = new PolyRef() { poly = C, trans = Mat3x3.Eye() }; if (set_at < 0) { polygon_lib.Add(pref); } else { polygon_lib[set_at] = pref; } return(set_at < 0 ? polygon_lib.Count - 1 : set_at); }
private static Ngons MSumConvex(Ngon pattern, Ngons subject, bool flip_pattern) { Ngon h_p = ConvexHull(pattern.Clone(0, 0, flip_pattern)); Ngon h_s = ConvexHull(subject[0].Clone()); int n_p = h_p.Count; int n_s = h_s.Count; int sp = 0; for (int k = 0; k < n_p; k++) { if (h_p[k].Y < h_p[sp].Y) { sp = k; } } int ss = 0; for (int k = 0; k < n_s; k++) { if (h_s[k].Y < h_s[ss].Y) { ss = k; } } Ngon poly = new Ngon(n_p + n_s); int i = 0; int j = 0; while (i < n_p || j < n_s) { int ip = (sp + i + 1) % n_p; int jp = (ss + j + 1) % n_s; int ii = (sp + i) % n_p; int jj = (ss + j) % n_s; IntPoint sum = new IntPoint(h_p[ii].X + h_s[jj].X, h_p[ii].Y + h_s[jj].Y); IntPoint v = new IntPoint(h_p[ip].X - h_p[ii].X, h_p[ip].Y - h_p[ii].Y); IntPoint w = new IntPoint(h_s[jp].X - h_s[jj].X, h_s[jp].Y - h_s[jj].Y); poly.Add(sum); if (i == n_p) { j++; continue; } if (j == n_s) { i++; continue; } long cross = v.Y * w.X - v.X * w.Y; if (cross < 0) { i++; } else if (cross > 0) { j++; } else { long dot = v.X * w.X + v.Y * w.Y; if (dot > 0) { i++; j++; } else { throw new Exception(); } } } return(Clipper.SimplifyPolygon(poly)); }
/// <summary> /// Append a set triangulated polygons to the nester and get handles for each point to the correp. polygon island /// </summary> /// <param name="points"></param> /// <param name="tris"></param> /// <returns></returns> public int[] AddPolygons(IntPoint[] points, int[] tris, double miter_distance = 0.0) { // from points to clusters of tris int[] poly_map = new int[points.Length]; for (int i = 0; i < poly_map.Length; i++) { poly_map[i] = -1; } HashSet <int>[] graph = new HashSet <int> [points.Length]; for (int i = 0; i < graph.Length; i++) { graph[i] = new HashSet <int>(); } for (int i = 0; i < tris.Length; i += 3) { int t1 = tris[i]; int t2 = tris[i + 1]; int t3 = tris[i + 2]; graph[t1].Add(t2); graph[t1].Add(t3); graph[t2].Add(t1); graph[t2].Add(t3); graph[t3].Add(t1); graph[t3].Add(t2); } if (graph.Any(p => p.Count == 0)) { throw new Exception("No singular vertices should exist on mesh"); } int[] clust_ids = new int[points.Length]; HashSet <int> unmarked = new HashSet <int>(Enumerable.Range(0, points.Length)); int clust_cnt = 0; while (unmarked.Count > 0) { Queue <int> open = new Queue <int>(); int first = unmarked.First(); unmarked.Remove(first); open.Enqueue(first); while (open.Count > 0) { int c = open.Dequeue(); clust_ids[c] = clust_cnt; foreach (int n in graph[c]) { if (unmarked.Contains(n)) { unmarked.Remove(n); open.Enqueue(n); } } } clust_cnt++; } Ngons[] clusters = new Ngons[clust_cnt]; for (int i = 0; i < tris.Length; i += 3) { int clust = clust_ids[tris[i]]; if (clusters[clust] == null) { clusters[clust] = new Ngons(); } IntPoint p1 = points[tris[i]]; IntPoint p2 = points[tris[i + 1]]; IntPoint p3 = points[tris[i + 2]]; clusters[clust].Add(new Ngon() { p1, p2, p3 }); } List <Ngons> fulls = new List <Ngons>(); for (int i = 0; i < clust_cnt; i++) { Ngons cl = clusters[i]; Clipper c = new Clipper(); foreach (Ngon n in cl) { c.AddPath(n, PolyType.ptSubject, true); } Ngons full = new Ngons(); c.Execute(ClipType.ctUnion, full, PolyFillType.pftNonZero); full = Clipper.SimplifyPolygons(full, PolyFillType.pftNonZero); if (miter_distance > 0.00001) { Ngons full_miter = new Ngons(); ClipperOffset co = new ClipperOffset(); co.AddPaths(full, JoinType.jtMiter, EndType.etClosedPolygon); co.Execute(ref full_miter, miter_distance); full_miter = Clipper.SimplifyPolygons(full_miter, PolyFillType.pftNonZero); fulls.Add(full_miter); } else { fulls.Add(full); } } for (int i = 0; i < clust_ids.Length; i++) { clust_ids[i] += polygon_lib.Count; } for (int i = 0; i < fulls.Count; i++) { polygon_lib.Add(new PolyRef() { poly = fulls[i], trans = Mat3x3.Eye() }); } return(clust_ids); }
/// <summary> /// Nest the collection of handles with minimal enclosing square from origin /// </summary> /// <param name="handles"></param> private void cmd_nest(params object[] param) { HashSet <int> unique = PreprocessHandles(param[0] as IEnumerable <int>); NFPQUALITY max_quality = (NFPQUALITY)param[1]; cmd_translate_origin_to_zero(unique); int n = unique.Count; Dictionary <int, IntRect> bounds = new Dictionary <int, IntRect>(); foreach (int handle in unique) { bounds.Add(handle, GeomUtility.GetBounds(polygon_lib[handle].GetTransformedPoly()[0])); } int[] ordered_handles = unique.OrderByDescending(p => Math.Max(bounds[p].Height(), bounds[p].Width())).ToArray(); double max_bound_area = bounds[ordered_handles[0]].Area(); int start_cnt = polygon_lib.Count; int[] canvas_regions = AddCanvasFitPolygon(ordered_handles); int base_cnt = polygon_lib.Count; for (int i = 0; i < n * n - n; i++) { polygon_lib.Add(new PolyRef()); } int update_breaks = 10; int nfp_chunk_sz = n * n / update_breaks * update_breaks == n * n ? n * n / update_breaks : n * n / update_breaks + 1; // the row corresponds to pattern and col to nfp for this pattern on col subj int[,] nfps = new int[n, n]; for (int k = 0; k < update_breaks; k++) { int start = k * nfp_chunk_sz; int end = Math.Min((k + 1) * nfp_chunk_sz, n * n); if (start >= end) { break; } Parallel.For(start, end, i => nfps[i / n, i % n] = i / n == i % n ? -1 : NFPKernel(ordered_handles[i % n], ordered_handles[i / n], max_bound_area, base_cnt + i - (i % n > i / n ? 1 : 0) - i / n, max_quality)); double progress = Math.Min(((double)(k + 1)) / (update_breaks + 1) * 50.0, 50.0); background_worker.ReportProgress((int)progress); if (background_worker.CancellationPending) { break; } } int place_chunk_sz = Math.Max(n / update_breaks, 1); bool[] placed = new bool[n]; for (int i = 0; i < n; i++) { if (i % 10 == 0 && background_worker.CancellationPending) { break; } Clipper c = new Clipper(); c.AddPath(polygon_lib[canvas_regions[i]].poly[0], PolyType.ptSubject, true); for (int j = 0; j < i; j++) { if (!placed[j]) { continue; } c.AddPaths(polygon_lib[nfps[i, j]].GetTransformedPoly(), PolyType.ptClip, true); } Ngons fit_region = new Ngons(); c.Execute(ClipType.ctDifference, fit_region, PolyFillType.pftNonZero); IntPoint o = polygon_lib[ordered_handles[i]].GetTransformedPoint(0, 0); IntRect bds = bounds[ordered_handles[i]]; long ext_x = bds.right - o.X; long ext_y = bds.top - o.Y; IntPoint place = new IntPoint(0, 0); long pl_score = long.MaxValue; for (int k = 0; k < fit_region.Count; k++) { for (int l = 0; l < fit_region[k].Count; l++) { IntPoint cand = fit_region[k][l]; long cd_score = Math.Max(cand.X + ext_x, cand.Y + ext_y); if (cd_score < pl_score) { pl_score = cd_score; place = cand; placed[i] = true; } } } if (!placed[i]) { continue; } cmd_translate(ordered_handles[i], (double)(place.X - o.X), (double)(place.Y - o.Y)); for (int k = i + 1; k < n; k++) { cmd_translate(nfps[k, i], (double)(place.X - o.X), (double)(place.Y - o.Y)); } if (i % place_chunk_sz == 0) { double progress = Math.Min(60.0 + ((double)(i / place_chunk_sz)) / (update_breaks + 1) * 40.0, 100.0); background_worker.ReportProgress((int)progress); } } // remove temporary added values polygon_lib.RemoveRange(start_cnt, polygon_lib.Count - start_cnt); }