Beispiel #1
0
        // offset tree recursively
        public static void offsetTree(NFP t, double offset, SvgNestConfig config, bool?inside = null)
        {
            var simple = t;

            simple = simplifyFunction(t, (inside == null) ? false : inside.Value);

            var offsetpaths = new NFP[] { simple };

            if (offset > 0)
            {
                offsetpaths = polygonOffsetDeepNest(simple, offset);
            }

            if (offsetpaths.Count() > 0)
            {
                List <SvgPoint> rett = new List <SvgPoint>();
                rett.AddRange(offsetpaths[0].Points);
                rett.AddRange(t.Points.Skip(t.length));
                t.Points = rett.ToArray();

                // replace array items in place

                //Array.prototype.splice.apply(t, [0, t.length].concat(offsetpaths[0]));
            }

            if (simple.children != null && simple.children.Count > 0)
            {
                if (t.children == null)
                {
                    t.children = new List <NFP>();
                }

                for (var i = 0; i < simple.children.Count; i++)
                {
                    t.children.Add(simple.children[i]);
                }
            }

            if (t.children != null && t.children.Count > 0)
            {
                for (var i = 0; i < t.children.Count; i++)
                {
                    offsetTree(t.children[i], -offset, config, (inside == null) ? true : (!inside));
                }
            }
        }
Beispiel #2
0
        public GeneticAlgorithm(NFP[] adam, SvgNestConfig config)
        {
            List <float> ang2 = new List <float>();

            for (int i = 0; i < adam.Length; i++)
            {
                ang2.Add((i * 90) % 360);
            }
            defaultAngles = ang2.ToArray();
            Config        = config;


            List <float> angles = new List <float>();

            for (int i = 0; i < adam.Length; i++)
            {
                if (StrictAngles)
                {
                    angles.Add(defaultAngles[i]);
                }
                else
                {
                    var angle = (float)Math.Floor(r.NextDouble() * Config.rotations) * (360f / Config.rotations);
                    angles.Add(angle);
                }

                //angles.Add(randomAngle(adam[i]));
            }


            population = new List <PopulationItem>();
            population.Add(new PopulationItem()
            {
                placements = adam.ToList(), Rotation = angles.ToArray()
            });
            while (population.Count() < config.populationSize)
            {
                var mutant = this.mutate(population[0]);
                population.Add(mutant);
            }
        }
Beispiel #3
0
        public static NFP simplifyFunction(NFP polygon, bool inside, SvgNestConfig config)
        {
            var tolerance = 4 * config.curveTolerance;

            // give special treatment to line segments above this length (squared)
            var fixedTolerance = 40 * config.curveTolerance * 40 * config.curveTolerance;
            int i, j, k;

            var hull = Background.getHull(polygon);

            if (config.simplify)
            {
                /*
                 *              // use convex hull
                 *              var hull = new ConvexHullGrahamScan();
                 *              for(var i=0; i<polygon.length; i++){
                 *                      hull.addPoint(polygon[i].x, polygon[i].y);
                 *              }
                 *
                 *              return hull.getHull();*/
                if (hull != null)
                {
                    return(hull);
                }
                else
                {
                    return(polygon);
                }
            }

            var cleaned = cleanPolygon2(polygon);

            if (cleaned != null && cleaned.length > 1)
            {
                polygon = cleaned;
            }
            else
            {
                return(polygon);
            }
            // polygon to polyline
            var copy = polygon.slice(0);

            copy.push(copy[0]);
            // mark all segments greater than ~0.25 in to be kept
            // the PD simplification algo doesn't care about the accuracy of long lines, only the absolute distance of each point
            // we care a great deal
            for (i = 0; i < copy.length - 1; i++)
            {
                var p1  = copy[i];
                var p2  = copy[i + 1];
                var sqd = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y);
                if (sqd > fixedTolerance)
                {
                    p1.marked = true;
                    p2.marked = true;
                }
            }

            var simple = Simplify.simplify(copy, tolerance, true);

            // now a polygon again
            //simple.pop();
            simple.Points = simple.Points.Take(simple.Points.Count() - 1).ToArray();

            // could be dirty again (self intersections and/or coincident points)
            simple = cleanPolygon2(simple);

            // simplification process reduced poly to a line or point
            if (simple == null)
            {
                simple = polygon;
            }

            var offsets = polygonOffsetDeepNest(simple, inside ? -tolerance : tolerance);

            NFP        offset     = null;
            double     offsetArea = 0;
            List <NFP> holes      = new List <NFP>();

            for (i = 0; i < offsets.Length; i++)
            {
                var area = GeometryUtil.polygonArea(offsets[i]);
                if (offset == null || area < offsetArea)
                {
                    offset     = offsets[i];
                    offsetArea = area;
                }
                if (area > 0)
                {
                    holes.Add(offsets[i]);
                }
            }

            // mark any points that are exact
            for (i = 0; i < simple.length; i++)
            {
                var seg = new NFP();
                seg.AddPoint(simple[i]);
                seg.AddPoint(simple[i + 1 == simple.length ? 0 : i + 1]);

                var index1 = find(seg[0], polygon);
                var index2 = find(seg[1], polygon);

                if (index1 + 1 == index2 || index2 + 1 == index1 || (index1 == 0 && index2 == polygon.length - 1) || (index2 == 0 && index1 == polygon.length - 1))
                {
                    seg[0].exact = true;
                    seg[1].exact = true;
                }
            }
            var numshells = 4;

            NFP[] shells = new NFP[numshells];

            for (j = 1; j < numshells; j++)
            {
                var delta = j * (tolerance / numshells);
                delta = inside ? -delta : delta;
                var shell = polygonOffsetDeepNest(simple, delta);
                if (shell.Count() > 0)
                {
                    shells[j] = shell.First();
                }
                else
                {
                    //shells[j] = shell;
                }
            }

            if (offset == null)
            {
                return(polygon);
            }
            // selective reversal of offset
            for (i = 0; i < offset.length; i++)
            {
                var o      = offset[i];
                var target = getTarget(o, simple, 2 * tolerance);

                // reverse point offset and try to find exterior points
                var test = clone(offset);
                test.Points[i] = new SvgPoint(target.x, target.y);

                if (!exterior(test, polygon, inside))
                {
                    o.x = target.x;
                    o.y = target.y;
                }
                else
                {
                    // a shell is an intermediate offset between simple and offset
                    for (j = 1; j < numshells; j++)
                    {
                        if (shells[j] != null)
                        {
                            var shell = shells[j];
                            var delta = j * (tolerance / numshells);
                            target         = getTarget(o, shell, 2 * delta);
                            test           = clone(offset);
                            test.Points[i] = new SvgPoint(target.x, target.y);
                            if (!exterior(test, polygon, inside))
                            {
                                o.x = target.x;
                                o.y = target.y;
                                break;
                            }
                        }
                    }
                }
            }

            // straighten long lines
            // a rounded rectangle would still have issues at this point, as the long sides won't line up straight

            var straightened = false;

            for (i = 0; i < offset.length; i++)
            {
                var p1 = offset[i];
                var p2 = offset[i + 1 == offset.length ? 0 : i + 1];

                var sqd = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y);

                if (sqd < fixedTolerance)
                {
                    continue;
                }
                for (j = 0; j < simple.length; j++)
                {
                    var s1 = simple[j];
                    var s2 = simple[j + 1 == simple.length ? 0 : j + 1];

                    var sqds = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y);

                    if (sqds < fixedTolerance)
                    {
                        continue;
                    }

                    if ((GeometryUtil._almostEqual(s1.x, s2.x) || GeometryUtil._almostEqual(s1.y, s2.y)) && // we only really care about vertical and horizontal lines
                        GeometryUtil._withinDistance(p1, s1, 2 * tolerance) &&
                        GeometryUtil._withinDistance(p2, s2, 2 * tolerance) &&
                        (!GeometryUtil._withinDistance(p1, s1, config.curveTolerance / 1000) ||
                         !GeometryUtil._withinDistance(p2, s2, config.curveTolerance / 1000)))
                    {
                        p1.x         = s1.x;
                        p1.y         = s1.y;
                        p2.x         = s2.x;
                        p2.y         = s2.y;
                        straightened = true;
                    }
                }
            }

            //if (straightened)
            {
                var Ac = _Clipper.ScaleUpPaths(offset, 10000000);
                var Bc = _Clipper.ScaleUpPaths(polygon, 10000000);

                var combined = new List <List <IntPoint> >();
                var clipper  = new ClipperLib.Clipper();

                clipper.AddPath(Ac.ToList(), ClipperLib.PolyType.ptSubject, true);
                clipper.AddPath(Bc.ToList(), ClipperLib.PolyType.ptSubject, true);

                // the line straightening may have made the offset smaller than the simplified
                if (clipper.Execute(ClipperLib.ClipType.ctUnion, combined, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero))
                {
                    double?largestArea = null;
                    for (i = 0; i < combined.Count; i++)
                    {
                        var n     = Background.toNestCoordinates(combined[i].ToArray(), 10000000);
                        var sarea = -GeometryUtil.polygonArea(n);
                        if (largestArea == null || largestArea < sarea)
                        {
                            offset      = n;
                            largestArea = sarea;
                        }
                    }
                }
            }

            cleaned = cleanPolygon2(offset);
            if (cleaned != null && cleaned.length > 1)
            {
                offset = cleaned;
            }

            #region experimental
            if (config.clipByHull)
            {
                offset = ClipSubject(offset, hull, config.clipperScale);
            }
            else if (config.clipByRects)
            {
                NFP rect1 = boundingBox(hull);
                offset = ClipSubject(offset, rect1, config.clipperScale);
                var mbox = GetMinimumBox(hull);
                offset = ClipSubject(offset, mbox, config.clipperScale);
            }
            #endregion

            // mark any points that are exact (for line merge detection)
            for (i = 0; i < offset.length; i++)
            {
                var seg    = new SvgPoint[] { offset[i], offset[i + 1 == offset.length ? 0 : i + 1] };
                var index1 = find(seg[0], polygon);
                var index2 = find(seg[1], polygon);
                if (index1 == null)
                {
                    index1 = 0;
                }
                if (index2 == null)
                {
                    index2 = 0;
                }
                if (index1 + 1 == index2 || index2 + 1 == index1 ||
                    (index1 == 0 && index2 == polygon.length - 1) ||
                    (index2 == 0 && index1 == polygon.length - 1))
                {
                    seg[0].exact = true;
                    seg[1].exact = true;
                }
            }

            if (!inside && holes != null && holes.Count > 0)
            {
                offset.children = holes;
            }

            return(offset);
        }