public List <Vector2[]> TriangulateClipperSolution(ClipperLib.PolyTree solution)
        {
            var tess = new Tess();

            tess.NoEmptyPolygons = true;

            // Add a contour for each part of the solution tree
            ClipperLib.PolyNode node = solution.GetFirst();
            while (node != null)
            {
                // Only interested in closed paths
                if (!node.IsOpen)
                {
                    // Add a new countor. Holes are automatically generated.
                    var vertices = node.Contour.Select(pt => new ContourVertex {
                        Position = new Vec3 {
                            X = pt.X, Y = pt.Y, Z = 0
                        }
                    }).ToArray();
                    tess.AddContour(vertices);
                }
                node = node.GetNext();
            }

            return(TrianglesFromTessellator(tess));
        }
Beispiel #2
0
 // Put the closed path polygons into an enumerable collection of an array of points.
 // Each array of points in a path in a "complex" polygon that supports convace edges and holes
 public static IEnumerable <PointF[]> SolutionPolygons_Complex(ClipperLib.PolyTree solution)
 {
     foreach (var points in ClipperLib.Clipper.ClosedPathsFromPolyTree(solution))
     {
         var pointfs = points.Select(pt => new PointF(pt.X, pt.Y));
         yield return(pointfs.ToArray());
     }
 }
        private XElement CreateCollisionElementForLayer(TmxLayer layer)
        {
            // Collision elements look like this
            // (Can also have EdgeCollider2Ds)
            //      <GameOject name="Collision">
            //        <PolygonCollider2D>
            //          <Path>list of points</Path>
            //          <Path>another list of points</Path>
            //        </PolygonCollider2D>
            //      </GameOject>

            LayerClipper.TransformPointFunc xfFunc =
                delegate(float x, float y)
            {
                // Transform point to Unity space
                PointF pointUnity3d       = PointFToUnityVector_NoScale(new PointF(x, y));
                ClipperLib.IntPoint point = new ClipperLib.IntPoint(pointUnity3d.X, pointUnity3d.Y);
                return(point);
            };

            LayerClipper.ProgressFunc progFunc =
                delegate(string prog)
            {
                Program.WriteLine(prog);
            };

            ClipperLib.PolyTree solution = LayerClipper.ExecuteClipper(this.tmxMap, layer, xfFunc, progFunc);

            // Add our polygon and edge colliders
            List <XElement> polyColliderElements = new List <XElement>();

            if (layer.IsExportingConvexPolygons())
            {
                AddPolygonCollider2DElements_Convex(solution, polyColliderElements);
            }
            else
            {
                AddPolygonCollider2DElements_Complex(solution, polyColliderElements);
            }

            AddEdgeCollider2DElements(ClipperLib.Clipper.OpenPathsFromPolyTree(solution), polyColliderElements);

            if (polyColliderElements.Count() == 0)
            {
                // No collisions on this layer
                return(null);
            }

            XElement gameObjectCollision =
                new XElement("GameObject",
                             new XAttribute("name", "Collision"),
                             polyColliderElements);

            return(gameObjectCollision);
        }
Beispiel #4
0
        // Put the closed path polygons into an enumerable collection of an array of points.
        // Each array of points in a separate convex polygon
        public static IEnumerable <PointF[]> SolutionPolygons_Simple(ClipperLib.PolyTree solution)
        {
            ConvexPolygonSet convexPolygonSet = new ConvexPolygonSet();

            convexPolygonSet.MakeConvextSetFromClipperSolution(solution);

            foreach (var polygon in convexPolygonSet.Polygons)
            {
                var pointfs = polygon.Select(pt => new PointF(pt.Xf, pt.Yf));
                yield return(pointfs.ToArray());
            }
        }
Beispiel #5
0
        private static void DrawCollisionLayer(SKCanvas canvas, TmxLayer tmxLayer, SKColor polyColor, SKColor lineColor)
        {
            LayerClipper.TransformPointFunc xfFunc   = (x, y) => new ClipperLib.IntPoint(x, y);
            LayerClipper.ProgressFunc       progFunc = (prog) => { }; // do nothing

            ClipperLib.PolyTree solution = LayerClipper.ExecuteClipper(tmxLayer.TmxMap, tmxLayer, xfFunc, progFunc);

            using (SKPaint paint = new SKPaint())
            {
                // Draw all closed polygons
                // First, add them to the path
                // (But are we using convex polygons are complex polygons?
                using (SKPath path = new SKPath())
                {
                    var polygons = tmxLayer.IsExportingConvexPolygons() ? LayerClipper.SolutionPolygons_Simple(solution) : LayerClipper.SolutionPolygons_Complex(solution);
                    foreach (var pointfArray in polygons)
                    {
                        var pts = pointfArray.ToSkPointArray();
                        path.AddPoly(pts, true);
                    }

                    // Then, fill and draw the path full of polygons
                    if (path.PointCount > 0)
                    {
                        paint.Style = SKPaintStyle.Fill;
                        paint.Color = polyColor;
                        canvas.DrawPath(path, paint);

                        paint.Style       = SKPaintStyle.Stroke;
                        paint.StrokeWidth = StrokeWidthThick;
                        paint.Color       = lineColor;
                        canvas.DrawPath(path, paint);
                    }
                }

                // Draw all lines (open polygons)
                using (SKPath path = new SKPath())
                {
                    foreach (var points in ClipperLib.Clipper.OpenPathsFromPolyTree(solution))
                    {
                        var pts = points.Select(pt => new SKPoint(pt.X, pt.Y)).ToArray();
                        path.AddPoly(pts, false);
                    }
                    if (path.PointCount > 0)
                    {
                        paint.Style       = SKPaintStyle.Stroke;
                        paint.StrokeWidth = StrokeWidthThick;
                        paint.Color       = lineColor;
                        canvas.DrawPath(path, paint);
                    }
                }
            }
        }
Beispiel #6
0
        private void DrawLayerColliders(Graphics g, TmxLayer layer, Color polyColor, Color lineColor)
        {
            LayerClipper.TransformPointFunc xfFunc   = (x, y) => new ClipperLib.IntPoint(x, y);
            LayerClipper.ProgressFunc       progFunc = (prog) => { }; // do nothing

            ClipperLib.PolyTree solution = LayerClipper.ExecuteClipper(this.tmxMap, layer, xfFunc, progFunc);

            float inverseScale = 1.0f / this.scale;

            if (inverseScale > 1)
            {
                inverseScale = 1;
            }

            using (GraphicsPath path = new GraphicsPath())
                using (Pen pen = new Pen(lineColor, 2.0f * inverseScale))
                    using (Brush brush = new HatchBrush(HatchStyle.Percent60, polyColor, Color.Transparent))
                    {
                        pen.Alignment = PenAlignment.Inset;

                        // Draw all closed polygons
                        // First, add them to the path
                        // (But are we using convex polygons are complex polygons?
                        var polygons = layer.IsExportingConvexPolygons() ? LayerClipper.SolutionPolygons_Simple(solution) : LayerClipper.SolutionPolygons_Complex(solution);
                        foreach (var pointfArray in polygons)
                        {
                            path.AddPolygon(pointfArray);
                        }

                        // Then, fill and draw the path full of polygons
                        if (path.PointCount > 0)
                        {
                            g.FillPath(brush, path);
                            g.DrawPath(pen, path);
                        }

                        // Draw all lines (open polygons)
                        path.Reset();
                        foreach (var points in ClipperLib.Clipper.OpenPathsFromPolyTree(solution))
                        {
                            var pointfs = points.Select(pt => new PointF(pt.X, pt.Y));
                            path.StartFigure();
                            path.AddLines(pointfs.ToArray());
                        }
                        if (path.PointCount > 0)
                        {
                            g.DrawPath(pen, path);
                        }
                    }
        }
Beispiel #7
0
        private RawCharacterOutline InternalGetCharacterOutlineForCaching(char textChar, FontX font)
        {
            var glyphTypeface = font.InvariantDescriptionStringWithoutSizeInformation;

            var rawOutline = GetRawCharacterOutline(textChar, font, FontSizeForCaching);

            var clipperPolygonsInput = new List <List <ClipperLib.IntPoint> >();

            var sharpPoints = new HashSet <ClipperLib.IntPoint>();
            var allPoints   = new HashSet <ClipperLib.IntPoint>(); // allPoints to determine whether after the simplification new points were added

            foreach (var polygon in rawOutline.Outline)
            {
                foreach (var p in polygon.SharpPoints)
                {
                    sharpPoints.Add(new ClipperLib.IntPoint(p.X * 65536, p.Y * 65536));
                }

                var clipperPolygon = new List <ClipperLib.IntPoint>(polygon.Points.Select((x) => new ClipperLib.IntPoint(x.X * 65536, x.Y * 65536)));
                clipperPolygonsInput.Add(clipperPolygon);

                foreach (var clipperPoint in clipperPolygon)
                {
                    allPoints.Add(clipperPoint);
                }
            }

            //clipperPolygons = ClipperLib.Clipper.SimplifyPolygons(clipperPolygons, ClipperLib.PolyFillType.pftEvenOdd);

            var clipperPolygons = new ClipperLib.PolyTree();
            var clipper         = new ClipperLib.Clipper
            {
                StrictlySimple = true
            };

            clipper.AddPaths(clipperPolygonsInput, ClipperLib.PolyType.ptSubject, true);
            clipper.Execute(ClipperLib.ClipType.ctUnion, clipperPolygons, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNegative);

            var polygons = new List <PolygonClosedD2D>();
            var dictClipperNodeToNode = new Dictionary <ClipperLib.PolyNode, PolygonClosedD2D>(); // helper dictionary

            ClipperPolyTreeToPolygonListRecursively(clipperPolygons, sharpPoints, allPoints, polygons, dictClipperNodeToNode);

            var result = rawOutline;

            result.Outline = polygons;

            return(result);
        }
        private void AddPolygonCollider2DElements_Convex(ClipperLib.PolyTree solution, List <XElement> xmlList)
        {
            // This may generate many convex polygons as opposed to one "complicated" one
            var polygons = LayerClipper.SolutionPolygons_Simple(solution);

            // Each PointF array is a polygon with a single path
            foreach (var pointfArray in polygons)
            {
                string   data        = String.Join(" ", pointfArray.Select(pt => String.Format("{0},{1}", pt.X * Tiled2Unity.Settings.Scale, pt.Y * Tiled2Unity.Settings.Scale)));
                XElement pathElement = new XElement("Path", data);

                XElement polyColliderElement = new XElement("PolygonCollider2D", pathElement);
                xmlList.Add(polyColliderElement);
            }
        }
Beispiel #9
0
        private List <Poly2Tri.DelaunayTriangle> GetTriangleListFromClipperSolution(ClipperLib.PolyTree solution)
        {
            Func <ClipperLib.IntPoint, Poly2Tri.PolygonPoint> xfToPolygonPoint = (p) => new Poly2Tri.PolygonPoint(p.X, p.Y);

            Poly2Tri.PolygonSet polygonSet = new Poly2Tri.PolygonSet();

            ClipperLib.PolyNode node = solution.GetFirst();
            while (node != null)
            {
                // Only interested in closed paths
                if (!node.IsOpen)
                {
                    if (node.IsHole)
                    {
                        if (polygonSet.Polygons.Count() > 0)
                        {
                            // Add hole to last polygon entered
                            var polyPoints        = node.Contour.Select(xfToPolygonPoint).ToArray();
                            Poly2Tri.Polygon hole = new Poly2Tri.Polygon(polyPoints);

                            Poly2Tri.Polygon polygon = polygonSet.Polygons.Last();
                            polygon.AddHole(hole);
                        }
                    }
                    else
                    {
                        // Add a new polygon to the set
                        var polyPoints           = node.Contour.Select(xfToPolygonPoint).ToList();
                        Poly2Tri.Polygon polygon = new Poly2Tri.Polygon(polyPoints);
                        polygonSet.Add(polygon);
                    }
                }
                node = node.GetNext();
            }

            // Now triangulate the whole set
            Poly2Tri.P2T.Triangulate(polygonSet);

            // Combine all the triangles into one list
            List <Poly2Tri.DelaunayTriangle> triangles = new List <Poly2Tri.DelaunayTriangle>();

            foreach (var polygon in polygonSet.Polygons)
            {
                triangles.AddRange(polygon.Triangles);
            }

            return(triangles);
        }
        public static long ExecuteOriginalClipper(int testIterationCount, List <ClipExecutionData> executionData)
        {
            var stopwatch = new Stopwatch();

            stopwatch.Start();

            for (var i = 0; i < testIterationCount; i++)
            {
                foreach (var clipPath in executionData)
                {
                    var subject = new List <List <ClipperLib.IntPoint> >(
                        clipPath
                        .Subject
                        .Select(poly => new List <ClipperLib.IntPoint>(poly.Select(pt =>
                                                                                   new ClipperLib.IntPoint(
                                                                                       pt.X * Scale,
                                                                                       pt.Y * Scale)))));

                    var clip = new List <List <ClipperLib.IntPoint> >(
                        clipPath
                        .Clip
                        .Select(poly => new List <ClipperLib.IntPoint>(poly.Select(pt =>
                                                                                   new ClipperLib.IntPoint(
                                                                                       pt.X * Scale,
                                                                                       pt.Y * Scale)))));

                    var solution = new ClipperLib.PolyTree();
                    var clipper  = new ClipperLib.Clipper();

                    clipper.AddPaths(subject, ClipperLib.PolyType.ptSubject, true);
                    clipper.AddPaths(clip, ClipperLib.PolyType.ptClip, true);

                    // Convert performance test library operation enum to ClipperLib operation enum.
                    var operation = (ClipperLib.ClipType)Enum.Parse(typeof(ClipperLib.ClipType), $"ct{clipPath.Operation}", true);
                    Assert.IsTrue(clipper.Execute(operation, solution));
                }
            }

            stopwatch.Stop();

            return(stopwatch.Elapsed.Ticks);
        }
Beispiel #11
0
        // Put the closed path polygons into an enumerable collection of an array of points.
        // Each array of points in a separate convex polygon
        public static IEnumerable <PointF[]> SolutionPolygons_Simple(ClipperLib.PolyTree solution)
        {
            // Triangulate the solution polygon
            Geometry.TriangulateClipperSolution triangulation = new Geometry.TriangulateClipperSolution();
            List <PointF[]> triangles = triangulation.Triangulate(solution);

#if T2U_TRIANGLES
            // Force triangle output
            foreach (var tri in triangles)
            {
                yield return(tri);
            }
#else
            // Group the triangles into convex polygons
            Geometry.ComposeConvexPolygons composition = new Geometry.ComposeConvexPolygons();
            List <PointF[]> polygons = composition.Compose(triangles);
            foreach (var poly in polygons)
            {
                yield return(poly);
            }
#endif
        }
        private void DrawLayerColliders(Graphics g, TmxLayer layer, Color polyColor, Color lineColor)
        {
            LayerClipper.TransformPointFunc xfFunc   = (x, y) => new ClipperLib.IntPoint(x, y);
            LayerClipper.ProgressFunc       progFunc = (prog) => { }; // do nothing

            ClipperLib.PolyTree solution = LayerClipper.ExecuteClipper(this.tmxMap, layer, xfFunc, progFunc);

            using (GraphicsPath path = new GraphicsPath())
                using (Pen pen = new Pen(lineColor, 1.0f))
                    using (Brush brush = new HatchBrush(HatchStyle.ForwardDiagonal, lineColor, polyColor))
                    {
                        pen.Alignment = PenAlignment.Inset;

                        // Draw all closed polygons
                        foreach (var points in ClipperLib.Clipper.ClosedPathsFromPolyTree(solution))
                        {
                            var pointfs = points.Select(pt => new PointF(pt.X, pt.Y));
                            path.AddPolygon(pointfs.ToArray());
                        }
                        if (path.PointCount > 0)
                        {
                            g.FillPath(brush, path);
                            g.DrawPath(pen, path);
                        }

                        // Draw all lines (open polygons)
                        path.Reset();
                        foreach (var points in ClipperLib.Clipper.OpenPathsFromPolyTree(solution))
                        {
                            var pointfs = points.Select(pt => new PointF(pt.X, pt.Y));
                            path.StartFigure();
                            path.AddLines(pointfs.ToArray());
                        }
                        if (path.PointCount > 0)
                        {
                            g.DrawPath(pen, path);
                        }
                    }
        }
        private void AddPolygonCollider2DElements_Complex(ClipperLib.PolyTree solution, List <XElement> xmlList)
        {
            // This should generate one "complicated" polygon which may contain holes and concave edges
            var polygons = ClipperLib.Clipper.ClosedPathsFromPolyTree(solution);

            if (polygons.Count == 0)
            {
                return;
            }

            // Add just one polygon collider that has all paths in it.
            List <XElement> pathElements = new List <XElement>();

            foreach (var path in polygons)
            {
                string   data        = String.Join(" ", path.Select(pt => String.Format("{0},{1}", pt.X * Tiled2Unity.Settings.Scale, pt.Y * Tiled2Unity.Settings.Scale)));
                XElement pathElement = new XElement("Path", data);
                pathElements.Add(pathElement);
            }

            XElement polyColliderElement = new XElement("PolygonCollider2D", pathElements);

            xmlList.Add(polyColliderElement);
        }
Beispiel #14
0
        public static List <PointLatLngAlt> CreateRotary(List <PointLatLngAlt> polygon, double altitude, double distance, double spacing, double angle, double overshoot1, double overshoot2, StartPosition startpos, bool shutter, float minLaneSeparation, float leadin, PointLatLngAlt HomeLocation, int clockwise_laps, bool match_spiral_perimeter, int laps)
        {
            spacing = 0;

            if (distance < 0.1)
            {
                distance = 0.1;
            }

            if (polygon.Count == 0)
            {
                return(new List <PointLatLngAlt>());
            }

            List <utmpos> ans = new List <utmpos>();

            // utm zone distance calcs will be done in
            int utmzone = polygon[0].GetUTMZone();

            // utm position list
            List <utmpos> utmpositions = utmpos.ToList(PointLatLngAlt.ToUTM(utmzone, polygon), utmzone);

            // close the loop if its not already
            if (utmpositions[0] != utmpositions[utmpositions.Count - 1])
            {
                utmpositions.Add(utmpositions[0]); // make a full loop
            }
            var maxlane = laps;                    // (Centroid(utmpositions).GetDistance(utmpositions[0]) / distance);

            ClipperLib.ClipperOffset clipperOffset = new ClipperLib.ClipperOffset();

            clipperOffset.AddPath(utmpositions.Select(a => { return(new ClipperLib.IntPoint(a.x * 1000.0, a.y * 1000.0)); }).ToList(), ClipperLib.JoinType.jtMiter, ClipperLib.EndType.etClosedPolygon);

            for (int lane = 0; lane < maxlane; lane++)
            {
                List <utmpos> ans1 = new List <utmpos>();

                ClipperLib.PolyTree tree = new ClipperLib.PolyTree();
                clipperOffset.Execute(ref tree, (Int64)(distance * 1000.0 * -lane));

                if (tree.ChildCount == 0)
                {
                    break;
                }

                if (lane < clockwise_laps || clockwise_laps < 0)
                {
                    ClipperLib.Clipper.ReversePaths(ClipperLib.Clipper.PolyTreeToPaths(tree));
                }

                foreach (var treeChild in tree.Childs)
                {
                    ans1 = treeChild.Contour.Select(a => new utmpos(a.X / 1000.0, a.Y / 1000.0, utmzone))
                           .ToList();

                    if (lane == 0 && clockwise_laps != 1 && match_spiral_perimeter)
                    {
                        ans1.Insert(0, ans1.Last <utmpos>());   // start at the last point of the first calculated lap
                                                                // to make a closed polygon on the first trip around
                    }


                    if (lane == clockwise_laps - 1)
                    {
                        ans1.Add(ans1.First <utmpos>());  // revisit the first waypoint on this lap to cleanly exit the CW pattern
                    }

                    if (ans.Count() > 2)
                    {
                        var start1 = ans[ans.Count() - 1];
                        var end1   = ans[ans.Count() - 2];

                        var start2 = ans1[0];
                        var end2   = ans1[ans1.Count() - 1];
                    }

                    ans.AddRange(ans1);
                }
            }

            // set the altitude on all points
            return(ans.Select(plla => { var a = plla.ToLLA(); a.Alt = altitude; a.Tag = "S"; return a; }).ToList());
        }
Beispiel #15
0
        public static ClipperLib.PolyTree ExecuteClipper(TmxMap tmxMap, TmxLayer tmxLayer, TransformPointFunc xfFunc, ProgressFunc progFunc)
        {
            // The "fullClipper" combines the clipper results from the smaller pieces
            ClipperLib.Clipper fullClipper = new ClipperLib.Clipper();

            // From the perspective of Clipper lines are polygons too
            // Closed paths == polygons
            // Open paths == lines
            var polygonGroups = from y in Enumerable.Range(0, tmxLayer.Height)
                                from x in Enumerable.Range(0, tmxLayer.Width)
                                let rawTileId = tmxLayer.GetRawTileIdAt(x, y)
                                let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId)
                                where tileId != 0
                                let tile = tmxMap.Tiles[tileId]
                                from polygon in tile.ObjectGroup.Objects
                                where (polygon as TmxHasPoints) != null
                                let groupX = x / LayerClipper.GroupBySize
                                let groupY = y / LayerClipper.GroupBySize
                                group new
                                {
                                    PositionOnMap = tmxMap.GetMapPositionAt(x, y, tile),
                                    HasPointsInterface = polygon as TmxHasPoints,
                                    TmxObjectInterface = polygon,
                                    IsFlippedDiagnoally = TmxMath.IsTileFlippedDiagonally(rawTileId),
                                    IsFlippedHorizontally = TmxMath.IsTileFlippedHorizontally(rawTileId),
                                    IsFlippedVertically = TmxMath.IsTileFlippedVertically(rawTileId),
                                    TileCenter = new PointF(tile.TileSize.Width * 0.5f, tile.TileSize.Height * 0.5f),
                                }
                                by Tuple.Create(groupX, groupY);

            int groupIndex = 0;
            int groupCount = polygonGroups.Count();

            foreach (var polyGroup in polygonGroups)
            {
                if (groupIndex % 5 == 0)
                {
                    progFunc(String.Format("Clipping '{0}' polygons: {1}%", tmxLayer.Name, (groupIndex / (float)groupCount) * 100));
                }
                groupIndex++;

                // The "groupClipper" clips the polygons in a smaller part of the world
                ClipperLib.Clipper groupClipper = new ClipperLib.Clipper();

                // Add all our polygons to the Clipper library so it can reduce all the polygons to a (hopefully small) number of paths
                foreach (var poly in polyGroup)
                {
                    // Create a clipper library polygon out of each and add it to our collection
                    ClipperPolygon clipperPolygon = new ClipperPolygon();

                    // Our points may be transformed due to tile flipping/rotation
                    // Before we transform them we put all the points into local space relative to the tile
                    SizeF offset = new SizeF(poly.TmxObjectInterface.Position);
                    PointF[] transformedPoints = poly.HasPointsInterface.Points.Select(pt => PointF.Add(pt, offset)).ToArray();

                    // Now transform the points relative to the tile
                    TmxMath.TransformPoints(transformedPoints, poly.TileCenter, poly.IsFlippedDiagnoally, poly.IsFlippedHorizontally, poly.IsFlippedVertically);

                    foreach (var pt in transformedPoints)
                    {
                        float x = poly.PositionOnMap.X + pt.X;
                        float y = poly.PositionOnMap.Y + pt.Y;

                        ClipperLib.IntPoint point = xfFunc(x, y);
                        clipperPolygon.Add(point);
                    }

                    // Because of Unity's cooridnate system, the winding order of the polygons must be reversed
                    clipperPolygon.Reverse();

                    // Add the "subject"
                    groupClipper.AddPath(clipperPolygon, ClipperLib.PolyType.ptSubject, poly.HasPointsInterface.ArePointsClosed());
                }

                // Get a solution for this group
                ClipperLib.PolyTree solution = new ClipperLib.PolyTree();
                groupClipper.Execute(ClipperLib.ClipType.ctUnion, solution, LayerClipper.SubjectFillRule, LayerClipper.ClipFillRule);

                // Combine the solutions into the full clipper
                fullClipper.AddPaths(ClipperLib.Clipper.ClosedPathsFromPolyTree(solution), ClipperLib.PolyType.ptSubject, true);
                fullClipper.AddPaths(ClipperLib.Clipper.OpenPathsFromPolyTree(solution), ClipperLib.PolyType.ptSubject, false);
            }
            progFunc(String.Format("Clipping '{0}' polygons: 100%", tmxLayer.Name));

            ClipperLib.PolyTree fullSolution = new ClipperLib.PolyTree();
            fullClipper.Execute(ClipperLib.ClipType.ctUnion, fullSolution, LayerClipper.SubjectFillRule, LayerClipper.ClipFillRule);

            return fullSolution;
        }
Beispiel #16
0
		private RawCharacterOutline InternalGetCharacterOutlineForCaching(char textChar, FontX font)
		{
			var glyphTypeface = font.InvariantDescriptionStringWithoutSizeInformation;

			var rawOutline = GetRawCharacterOutline(textChar, font, FontSizeForCaching);

			List<List<ClipperLib.IntPoint>> clipperPolygonsInput = new List<List<ClipperLib.IntPoint>>();

			var sharpPoints = new HashSet<ClipperLib.IntPoint>();
			var allPoints = new HashSet<ClipperLib.IntPoint>(); // allPoints to determine whether after the simplification new points were added

			foreach (var polygon in rawOutline.Outline)
			{
				foreach (var p in polygon.SharpPoints)
				{
					sharpPoints.Add(new ClipperLib.IntPoint(p.X * 65536, p.Y * 65536));
				}

				var clipperPolygon = new List<ClipperLib.IntPoint>(polygon.Points.Select((x) => new ClipperLib.IntPoint(x.X * 65536, x.Y * 65536)));
				clipperPolygonsInput.Add(clipperPolygon);

				foreach (var clipperPoint in clipperPolygon)
					allPoints.Add(clipperPoint);
			}

			//clipperPolygons = ClipperLib.Clipper.SimplifyPolygons(clipperPolygons, ClipperLib.PolyFillType.pftEvenOdd);

			var clipperPolygons = new ClipperLib.PolyTree();
			ClipperLib.Clipper clipper = new ClipperLib.Clipper();
			clipper.StrictlySimple = true;
			clipper.AddPaths(clipperPolygonsInput, ClipperLib.PolyType.ptSubject, true);
			clipper.Execute(ClipperLib.ClipType.ctUnion, clipperPolygons, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNegative);

			var polygons = new List<PolygonClosedD2D>();
			var dictClipperNodeToNode = new Dictionary<ClipperLib.PolyNode, PolygonClosedD2D>(); // helper dictionary
			ClipperPolyTreeToPolygonListRecursively(clipperPolygons, sharpPoints, allPoints, polygons, dictClipperNodeToNode);

			var result = rawOutline;
			result.Outline = polygons;

			return result;
		}
Beispiel #17
0
        public static List <PointLatLngAlt> CreateRotary(List <PointLatLngAlt> polygon, double altitude, double distance, double spacing, double angle, double overshoot1, double overshoot2, StartPosition startpos, bool shutter, float minLaneSeparation, float leadin, PointLatLngAlt HomeLocation)
        {
            spacing = 0;

            if (distance < 0.1)
            {
                distance = 0.1;
            }

            if (polygon.Count == 0)
            {
                return(new List <PointLatLngAlt>());
            }

            List <utmpos> ans = new List <utmpos>();

            // utm zone distance calcs will be done in
            int utmzone = polygon[0].GetUTMZone();

            // utm position list
            List <utmpos> utmpositions = utmpos.ToList(PointLatLngAlt.ToUTM(utmzone, polygon), utmzone);

            // close the loop if its not already
            if (utmpositions[0] != utmpositions[utmpositions.Count - 1])
            {
                utmpositions.Add(utmpositions[0]); // make a full loop
            }
            var maxlane = 200;                     // (Centroid(utmpositions).GetDistance(utmpositions[0]) / distance);

            ClipperLib.ClipperOffset clipperOffset = new ClipperLib.ClipperOffset();

            clipperOffset.AddPath(utmpositions.Select(a => { return(new ClipperLib.IntPoint(a.x * 1000.0, a.y * 1000.0)); }).ToList(), ClipperLib.JoinType.jtMiter, ClipperLib.EndType.etClosedPolygon);

            for (int lane = 0; lane < maxlane; lane++)
            {
                List <utmpos> ans1 = new List <utmpos>();

                ClipperLib.PolyTree tree = new ClipperLib.PolyTree();
                clipperOffset.Execute(ref tree, (Int64)(distance * 1000.0 * -lane));

                if (tree.ChildCount == 0)
                {
                    break;
                }

                foreach (var treeChild in tree.Childs)
                {
                    ans1 = treeChild.Contour.Select(a => new utmpos(a.X / 1000.0, a.Y / 1000.0, utmzone))
                           .ToList();

                    if (ans.Count() > 2)
                    {
                        var start1 = ans[ans.Count() - 1];
                        var end1   = ans[ans.Count() - 2];

                        var start2 = ans1[0];
                        var end2   = ans1[ans1.Count() - 1];
                    }

                    ans.AddRange(ans1);
                }
            }

            // set the altitude on all points
            return(ans.Select(plla => { var a = plla.ToLLA(); a.Alt = altitude; a.Tag = "S"; return a; }).ToList());
        }
Beispiel #18
0
        public static ClipperLib.PolyTree ExecuteClipper(TmxMap tmxMap, TmxLayer tmxLayer, TransformPointFunc xfFunc, ProgressFunc progFunc)
        {
            // The "fullClipper" combines the clipper results from the smaller pieces
            ClipperLib.Clipper fullClipper = new ClipperLib.Clipper();

            // From the perspective of Clipper lines are polygons too
            // Closed paths == polygons
            // Open paths == lines
            var polygonGroups = from y in Enumerable.Range(0, tmxLayer.Height)
                                from x in Enumerable.Range(0, tmxLayer.Width)
                                let rawTileId = tmxLayer.GetRawTileIdAt(x, y)
                                                let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId)
                                                             where tileId != 0
                                                             let tile = tmxMap.Tiles[tileId]
                                                                        from polygon in tile.ObjectGroup.Objects
                                                                        where (polygon as TmxHasPoints) != null
                                                                        let groupX = x / LayerClipper.GroupBySize
                                                                                     let groupY = y / LayerClipper.GroupBySize
                                                                                                  group new
            {
                PositionOnMap         = tmxMap.GetMapPositionAt(x, y, tile),
                HasPointsInterface    = polygon as TmxHasPoints,
                TmxObjectInterface    = polygon,
                IsFlippedDiagnoally   = TmxMath.IsTileFlippedDiagonally(rawTileId),
                IsFlippedHorizontally = TmxMath.IsTileFlippedHorizontally(rawTileId),
                IsFlippedVertically   = TmxMath.IsTileFlippedVertically(rawTileId),
                TileCenter            = new PointF(tile.TileSize.Width * 0.5f, tile.TileSize.Height * 0.5f),
            }
            by Tuple.Create(groupX, groupY);

            int groupIndex = 0;
            int groupCount = polygonGroups.Count();

            foreach (var polyGroup in polygonGroups)
            {
                if (groupIndex % 5 == 0)
                {
                    progFunc(String.Format("Clipping '{0}' polygons: {1}%", tmxLayer.Name, (groupIndex / (float)groupCount) * 100));
                }
                groupIndex++;

                // The "groupClipper" clips the polygons in a smaller part of the world
                ClipperLib.Clipper groupClipper = new ClipperLib.Clipper();

                // Add all our polygons to the Clipper library so it can reduce all the polygons to a (hopefully small) number of paths
                foreach (var poly in polyGroup)
                {
                    // Create a clipper library polygon out of each and add it to our collection
                    ClipperPolygon clipperPolygon = new ClipperPolygon();

                    // Our points may be transformed due to tile flipping/rotation
                    // Before we transform them we put all the points into local space relative to the tile
                    SizeF    offset            = new SizeF(poly.TmxObjectInterface.Position);
                    PointF[] transformedPoints = poly.HasPointsInterface.Points.Select(pt => PointF.Add(pt, offset)).ToArray();

                    // Now transform the points relative to the tile
                    TmxMath.TransformPoints(transformedPoints, poly.TileCenter, poly.IsFlippedDiagnoally, poly.IsFlippedHorizontally, poly.IsFlippedVertically);

                    foreach (var pt in transformedPoints)
                    {
                        float x = poly.PositionOnMap.X + pt.X;
                        float y = poly.PositionOnMap.Y + pt.Y;

                        ClipperLib.IntPoint point = xfFunc(x, y);
                        clipperPolygon.Add(point);
                    }

                    // Because of Unity's cooridnate system, the winding order of the polygons must be reversed
                    clipperPolygon.Reverse();

                    // Add the "subject"
                    groupClipper.AddPath(clipperPolygon, ClipperLib.PolyType.ptSubject, poly.HasPointsInterface.ArePointsClosed());
                }

                // Get a solution for this group
                ClipperLib.PolyTree solution = new ClipperLib.PolyTree();
                groupClipper.Execute(ClipperLib.ClipType.ctUnion, solution, LayerClipper.SubjectFillRule, LayerClipper.ClipFillRule);

                // Combine the solutions into the full clipper
                fullClipper.AddPaths(ClipperLib.Clipper.ClosedPathsFromPolyTree(solution), ClipperLib.PolyType.ptSubject, true);
                fullClipper.AddPaths(ClipperLib.Clipper.OpenPathsFromPolyTree(solution), ClipperLib.PolyType.ptSubject, false);
            }
            progFunc(String.Format("Clipping '{0}' polygons: 100%", tmxLayer.Name));

            ClipperLib.PolyTree fullSolution = new ClipperLib.PolyTree();
            fullClipper.Execute(ClipperLib.ClipType.ctUnion, fullSolution, LayerClipper.SubjectFillRule, LayerClipper.ClipFillRule);

            return(fullSolution);
        }
Beispiel #19
0
        public List <PointF[]> Triangulate(ClipperLib.PolyTree solution)
        {
            List <PointF[]> triangles = new List <PointF[]>();

            var tess = new LibTessDotNet.Tess();

            tess.NoEmptyPolygons = true;

            // Transformation function from ClipperLip Point to LibTess contour vertex
            Func <ClipperLib.IntPoint, LibTessDotNet.ContourVertex> xfToContourVertex = (p) => new LibTessDotNet.ContourVertex()
            {
                Position = new LibTessDotNet.Vec3 {
                    X = p.X, Y = p.Y, Z = 0
                }
            };

            // Add a contour for each part of the solution tree
            ClipperLib.PolyNode node = solution.GetFirst();
            while (node != null)
            {
                // Only interested in closed paths
                if (!node.IsOpen)
                {
                    // Add a new countor. Holes are automatically generated.
                    var vertices = node.Contour.Select(xfToContourVertex).ToArray();
                    tess.AddContour(vertices);
                }
                node = node.GetNext();
            }

            // Do the tessellation
            tess.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons, 3);

            // Extract the triangles
            int numTriangles = tess.ElementCount;

            for (int i = 0; i < numTriangles; i++)
            {
                var v0 = tess.Vertices[tess.Elements[i * 3 + 0]].Position;
                var v1 = tess.Vertices[tess.Elements[i * 3 + 1]].Position;
                var v2 = tess.Vertices[tess.Elements[i * 3 + 2]].Position;

                List <PointF> triangle = new List <PointF>()
                {
                    new PointF(v0.X, v0.Y),
                    new PointF(v1.X, v1.Y),
                    new PointF(v2.X, v2.Y),
                };

                // Assre each triangle needs to be CCW
                float cross = Geometry.Math.Cross(triangle[0], triangle[1], triangle[2]);
                if (cross > 0)
                {
                    triangle.Reverse();
                }

                triangles.Add(triangle.ToArray());
            }

            return(triangles);
        }
Beispiel #20
0
        public static ClipperLib.PolyTree ExecuteClipper(TmxMap tmxMap, TmxLayer tmxLayer, TransformPointFunc xfFunc, ProgressFunc progFunc)
        {
            // The "fullClipper" combines the clipper results from the smaller pieces
            ClipperLib.Clipper fullClipper = new ClipperLib.Clipper();

            // Limit to polygon "type" that matches the collision layer name (unless we are overriding the whole layer to a specific Unity Layer Name)
            bool usingUnityLayerOverride = !String.IsNullOrEmpty(tmxLayer.UnityLayerOverrideName);

            // From the perspective of Clipper lines are polygons too
            // Closed paths == polygons
            // Open paths == lines
            Dictionary <TupleInt2, List <PolygonGroup> > polygonGroups = new Dictionary <TupleInt2, List <PolygonGroup> >();

            foreach (int y in Enumerable.Range(0, tmxLayer.Height))
            {
                foreach (int x in Enumerable.Range(0, tmxLayer.Width))
                {
                    uint rawTileId = tmxLayer.GetRawTileIdAt(x, y);
                    if (rawTileId == 0)
                    {
                        continue;
                    }

                    uint    tileId = TmxMath.GetTileIdWithoutFlags(rawTileId);
                    TmxTile tile   = tmxMap.Tiles[tileId];

                    foreach (TmxObject polygon in tile.ObjectGroup.Objects)
                    {
                        if (typeof(TmxHasPoints).IsAssignableFrom(polygon.GetType()) &&
                            (usingUnityLayerOverride || String.Compare(polygon.Type, tmxLayer.Name, true) == 0))
                        {
                            int groupX = x / LayerClipper.GroupBySize;
                            int groupY = y / LayerClipper.GroupBySize;

                            PolygonGroup poly = new PolygonGroup();
                            poly.PositionOnMap         = tmxMap.GetMapPositionAt(x, y, tile);
                            poly.HasPointsInterface    = polygon as TmxHasPoints;
                            poly.TmxObjectInterface    = polygon;
                            poly.IsFlippedDiagnoally   = TmxMath.IsTileFlippedDiagonally(rawTileId);
                            poly.IsFlippedHorizontally = TmxMath.IsTileFlippedHorizontally(rawTileId);
                            poly.IsFlippedVertically   = TmxMath.IsTileFlippedVertically(rawTileId);
                            poly.TileCenter            = new PointF(tile.TileSize.Width * 0.5f, tile.TileSize.Height * 0.5f);

                            TupleInt2 key = new TupleInt2(groupX, groupY);
                            if (!polygonGroups.ContainsKey(key))
                            {
                                polygonGroups[key] = new List <PolygonGroup>();
                            }
                            polygonGroups[key].Add(poly);
                        }
                    }
                }
            }
            // Tuple not supported in Mono 2.0 so doing this the old fashioned way, sorry
            //var polygonGroups = from y in Enumerable.Range(0, tmxLayer.Height)
            //                    from x in Enumerable.Range(0, tmxLayer.Width)
            //                    let rawTileId = tmxLayer.GetRawTileIdAt(x, y)
            //                    where rawTileId != 0
            //                    let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId)
            //                    let tile = tmxMap.Tiles[tileId]
            //                    from polygon in tile.ObjectGroup.Objects
            //                    where (polygon as TmxHasPoints) != null
            //                    where  usingUnityLayerOverride || String.Compare(polygon.Type, tmxLayer.Name, true) == 0
            //                    let groupX = x / LayerClipper.GroupBySize
            //                    let groupY = y / LayerClipper.GroupBySize
            //                    group new
            //                    {
            //                        PositionOnMap = tmxMap.GetMapPositionAt(x, y, tile),
            //                        HasPointsInterface = polygon as TmxHasPoints,
            //                        TmxObjectInterface = polygon,
            //                        IsFlippedDiagnoally = TmxMath.IsTileFlippedDiagonally(rawTileId),
            //                        IsFlippedHorizontally = TmxMath.IsTileFlippedHorizontally(rawTileId),
            //                        IsFlippedVertically = TmxMath.IsTileFlippedVertically(rawTileId),
            //                        TileCenter = new PointF(tile.TileSize.Width * 0.5f, tile.TileSize.Height * 0.5f),
            //                    }
            //                    by Tuple.Create(groupX, groupY);

            int groupIndex = 0;
            int groupCount = polygonGroups.Count();

            foreach (TupleInt2 key in polygonGroups.Keys)
            {
                if (groupIndex % 5 == 0)
                {
                    progFunc(String.Format("Clipping '{0}' polygons: {1}%", tmxLayer.Name, (groupIndex / (float)groupCount) * 100));
                }
                groupIndex++;

                // The "groupClipper" clips the polygons in a smaller part of the world
                ClipperLib.Clipper groupClipper = new ClipperLib.Clipper();

                // Add all our polygons to the Clipper library so it can reduce all the polygons to a (hopefully small) number of paths
                foreach (PolygonGroup poly in polygonGroups[key])
                {
                    // Create a clipper library polygon out of each and add it to our collection
                    ClipperPolygon clipperPolygon = new ClipperPolygon();

                    // Our points may be transformed due to tile flipping/rotation
                    // Before we transform them we put all the points into local space relative to the tile
                    SizeF    offset            = new SizeF(poly.TmxObjectInterface.Position);
                    PointF[] transformedPoints = poly.HasPointsInterface.Points.Select(pt => PointF.Add(pt, offset)).ToArray();

                    // Now transform the points relative to the tile
                    TmxMath.TransformPoints(transformedPoints, poly.TileCenter, poly.IsFlippedDiagnoally, poly.IsFlippedHorizontally, poly.IsFlippedVertically);

                    foreach (var pt in transformedPoints)
                    {
                        float x = poly.PositionOnMap.X + pt.X;
                        float y = poly.PositionOnMap.Y + pt.Y;

                        ClipperLib.IntPoint point = xfFunc(x, y);
                        clipperPolygon.Add(point);
                    }

                    // Because of Unity's cooridnate system, the winding order of the polygons must be reversed
                    clipperPolygon.Reverse();

                    // Add the "subject"
                    groupClipper.AddPath(clipperPolygon, ClipperLib.PolyType.ptSubject, poly.HasPointsInterface.ArePointsClosed());
                }

                // Get a solution for this group
                ClipperLib.PolyTree solution = new ClipperLib.PolyTree();
                groupClipper.Execute(ClipperLib.ClipType.ctUnion, solution, LayerClipper.SubjectFillRule, LayerClipper.ClipFillRule);

                // Combine the solutions into the full clipper
                fullClipper.AddPaths(ClipperLib.Clipper.ClosedPathsFromPolyTree(solution), ClipperLib.PolyType.ptSubject, true);
                fullClipper.AddPaths(ClipperLib.Clipper.OpenPathsFromPolyTree(solution), ClipperLib.PolyType.ptSubject, false);
            }
            progFunc(String.Format("Clipping '{0}' polygons: 100%", tmxLayer.Name));

            ClipperLib.PolyTree fullSolution = new ClipperLib.PolyTree();
            fullClipper.Execute(ClipperLib.ClipType.ctUnion, fullSolution, LayerClipper.SubjectFillRule, LayerClipper.ClipFillRule);

            return(fullSolution);
        }
Beispiel #21
0
        public void MakeConvextSetFromClipperSolution(ClipperLib.PolyTree solution)
        {
            var triangles = GetTriangleListFromClipperSolution(solution);

            MakeConvexSetFromTriangles(triangles);
        }
Beispiel #22
0
        private void FixAndSetShapes(IShape[] outlines, IShape[] holes)
        {
            // if any outline doesn't overlap another shape then we don't have to bother with sending them through clipper
            // as sending then though clipper will turn them into generic polygons and loose thier shape specific optimisations
            int outlineLength = outlines.Length;
            int holesLength   = holes?.Length ?? 0;

            bool[] overlappingOutlines    = new bool[outlineLength];
            bool[] overlappingHoles       = new bool[holesLength];
            bool   anyOutlinesOverlapping = false;
            bool   anyHolesOverlapping    = false;

            for (int i = 0; i < outlineLength; i++)
            {
                for (int j = i + 1; j < outlineLength; j++)
                {
                    // skip the bounds check if they are already tested
                    if (overlappingOutlines[i] == false || overlappingOutlines[j] == false)
                    {
                        if (this.OverlappingBoundingBoxes(outlines[i], outlines[j]))
                        {
                            overlappingOutlines[i] = true;
                            overlappingOutlines[j] = true;
                            anyOutlinesOverlapping = true;
                        }
                    }
                }

                for (int k = 0; k < holesLength; k++)
                {
                    if (overlappingOutlines[i] == false || overlappingHoles[k] == false)
                    {
                        if (this.OverlappingBoundingBoxes(outlines[i], holes[k]))
                        {
                            overlappingOutlines[i] = true;
                            overlappingHoles[k]    = true;
                            anyOutlinesOverlapping = true;
                            anyHolesOverlapping    = true;
                        }
                    }
                }
            }

            if (anyOutlinesOverlapping)
            {
                var clipper = new ClipperLib.Clipper();

                // add the outlines and the holes to clipper, scaling up from the float source to the int based system clipper uses
                this.AddPoints(clipper, outlines, overlappingOutlines, ClipperLib.PolyType.ptSubject);
                if (anyHolesOverlapping)
                {
                    this.AddPoints(clipper, holes, overlappingHoles, ClipperLib.PolyType.ptClip);
                }

                var tree = new ClipperLib.PolyTree();
                clipper.Execute(ClipperLib.ClipType.ctDifference, tree);

                List <IShape> newShapes = new List <IShape>();

                // convert the 'tree' back to shapes
                this.ExtractOutlines(tree, newShapes);

                // add the origional outlines that where not overlapping
                for (int i = 0; i < outlineLength - 1; i++)
                {
                    if (!overlappingOutlines[i])
                    {
                        newShapes.Add(outlines[i]);
                    }
                }

                this.shapes = newShapes.ToArray();
            }
            else
            {
                this.shapes = outlines;
            }

            var paths = new List <IPath>();

            foreach (var o in this.shapes)
            {
                paths.AddRange(o);
            }

            this.paths = paths;
        }
        private XElement CreateCollisionElementForLayer(TmxLayer layer)
        {
            // Collision elements look like this
            // (Can also have EdgeCollider2Ds)
            //      <GameOject name="Collision">
            //        <PolygonCollider2D>
            //          <Path>list of points</Path>
            //          <Path>another list of points</Path>
            //        </PolygonCollider2D>
            //      </GameOject>

            LayerClipper.TransformPointFunc xfFunc =
                delegate(float x, float y)
            {
                // Transform point to Unity space
                PointF pointUnity3d       = PointFToUnityVector_NoScale(new PointF(x, y));
                ClipperLib.IntPoint point = new ClipperLib.IntPoint(pointUnity3d.X, pointUnity3d.Y);
                return(point);
            };

            LayerClipper.ProgressFunc progFunc =
                delegate(string prog)
            {
                Logger.WriteLine(prog);
            };

            ClipperLib.PolyTree solution = LayerClipper.ExecuteClipper(this.tmxMap, layer, xfFunc, progFunc);

            var paths = ClipperLib.Clipper.ClosedPathsFromPolyTree(solution);

            if (paths.Count >= MaxNumberOfSafePaths)
            {
                StringBuilder warning = new StringBuilder();
                warning.AppendFormat("Layer '{0}' has a large number of polygon paths ({1}).", layer.Name, paths.Count);
                warning.AppendLine("  Importing this layer may be slow in Unity. (Can take an hour or more for +1000 paths.)");
                warning.AppendLine("  Check polygon/rectangle objects in Tile Collision Editor in Tiled and use 'Snap to Grid' or 'Snap to Fine Grid'.");
                warning.AppendLine("  You want colliders to be set up so they can be merged with colliders on neighboring tiles, reducing path count considerably.");
                warning.AppendLine("  In some cases the size of the map may need to be reduced.");
                Logger.WriteWarning(warning.ToString());
            }

            // Add our polygon and edge colliders
            List <XElement> polyColliderElements = new List <XElement>();

            if (layer.IsExportingConvexPolygons())
            {
                AddPolygonCollider2DElements_Convex(solution, polyColliderElements);
            }
            else
            {
                AddPolygonCollider2DElements_Complex(solution, polyColliderElements);
            }

            AddEdgeCollider2DElements(ClipperLib.Clipper.OpenPathsFromPolyTree(solution), polyColliderElements);

            if (polyColliderElements.Count() == 0)
            {
                // No collisions on this layer
                return(null);
            }

            XElement gameObjectCollision =
                new XElement("GameObject",
                             new XAttribute("name", "Collision"),
                             polyColliderElements);

            // Collision layer may have a name and "unity physics layer" to go with it
            // (But not if we're using unity:layer override)
            if (String.IsNullOrEmpty(layer.UnityLayerOverrideName) && !String.IsNullOrEmpty(layer.Name))
            {
                gameObjectCollision.SetAttributeValue("name", "Collision_" + layer.Name);
                gameObjectCollision.SetAttributeValue("layer", layer.Name);
            }

            return(gameObjectCollision);
        }