/// <summary> /// Get the optimal quality for tradeoff between speed and precision of NFP /// </summary> /// <param name="subj_handle"></param> /// <param name="pattern_handle"></param> /// <returns></returns> private NFPQUALITY GetNFPQuality(int subj_handle, int pattern_handle, double max_area_bounds) { Ngon S = polygon_lib[subj_handle].GetTransformedPoly()[0]; Ngon P = polygon_lib[pattern_handle].GetTransformedPoly()[0]; if (GeomUtility.AlmostRectangle(S) && GeomUtility.AlmostRectangle(P)) { return(NFPQUALITY.Simple); } double s_A = GeomUtility.GetBounds(S).Area(); double p_A = GeomUtility.GetBounds(P).Area(); if (p_A / s_A > 1000) { return(NFPQUALITY.Simple); } if (s_A / max_area_bounds < 0.05) { return(NFPQUALITY.Simple); } if (p_A / s_A > 100) { return(NFPQUALITY.Convex); } if (p_A / s_A > 50) { return(NFPQUALITY.ConcaveLight); } if (p_A / s_A > 10) { return(NFPQUALITY.ConcaveMedium); } if (p_A / s_A > 2) { return(NFPQUALITY.ConcaveHigh); } if (p_A / s_A > 0.25) { return(NFPQUALITY.ConcaveFull); } return(NFPQUALITY.Full); }
public int AddCanvasFitPolygon(IntRect canvas, int pattern_handle) { Ngon B = polygon_lib[pattern_handle].GetTransformedPoly()[0]; Ngon C = GeomUtility.CanFitInsidePolygon(canvas, B); polygon_lib.Add(new PolyRef() { poly = new Ngons() { C }, trans = Mat3x3.Eye() }); return(polygon_lib.Count - 1); }
public int[] AddCanvasFitPolygon(IEnumerable <int> handles) { HashSet <int> unique = PreprocessHandles(handles); long w = 0; long h = 0; foreach (int i in unique) { IntRect bds = GeomUtility.GetBounds(polygon_lib[i].GetTransformedPoly()[0]); w += bds.Width(); h += bds.Height(); } w += 1000; h += 1000; IntRect canvas = new IntRect(0, h, w, 0); return(handles.Select(p => AddCanvasFitPolygon(canvas, p)).ToArray()); }
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 void cmd_optimal_rotation(int handle) { Ngon hull = polygon_lib[handle].GetTransformedPoly()[0]; int n = hull.Count; double best_t = 0; int best = 0; long best_area = long.MaxValue; bool flip_best = false; for (int i = 0; i < n; i++) { double t = GeomUtility.AlignToEdgeRotation(hull, i); Mat3x3 rot = Mat3x3.RotateCounterClockwise(t); Ngon clone = hull.Clone(rot); IntRect bounds = GeomUtility.GetBounds(clone); long area = bounds.Area(); double aspect = bounds.Aspect(); if (area < best_area) { best_area = area; best = i; best_t = t; flip_best = aspect > 1.0; } } double flip = flip_best ? Math.PI * 0.5 : 0; IntPoint around = hull[best]; cmd_translate(handle, (double)-around.X, (double)-around.Y); cmd_rotate(handle, best_t + flip); cmd_translate(handle, (double)around.X, (double)around.Y); }
private void cmd_refit(params object[] param) { Rect64 target = (Rect64)param[0]; bool stretch = (bool)param[1]; HashSet <int> unique = PreprocessHandles(param[2] as IEnumerable <int>); HashSet <Vector64> points = new HashSet <Vector64>(); foreach (int i in unique) { points.UnionWith(polygon_lib[i].poly[0].Select(p => polygon_lib[i].trans * new Vector64(p.X, p.Y))); } Vector64 scale, trans; GeomUtility.GetRefitTransform(points, target, stretch, out scale, out trans); foreach (int i in unique) { cmd_scale(i, scale.X, scale.Y); cmd_translate(i, trans.X, trans.Y); } }
/// <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); }