/// <summary> /// Conduct a clip operation on this profile. /// </summary> internal void Clip(IEnumerable <Profile> additionalHoles = null) { var clipper = new ClipperLib.Clipper(); clipper.AddPath(this.Perimeter.ToClipperPath(), ClipperLib.PolyType.ptSubject, true); if (this.Voids != null) { clipper.AddPaths(this.Voids.Select(p => p.ToClipperPath()).ToList(), ClipperLib.PolyType.ptClip, true); } if (additionalHoles != null) { clipper.AddPaths(additionalHoles.Select(h => h.Perimeter.ToClipperPath()).ToList(), ClipperLib.PolyType.ptClip, true); } var solution = new List <List <ClipperLib.IntPoint> >(); var result = clipper.Execute(ClipperLib.ClipType.ctDifference, solution, ClipperLib.PolyFillType.pftEvenOdd); // Completely disjoint polygons like a circular pipe // profile will result in an empty solution. if (solution.Count > 0) { var polys = solution.Select(s => s.ToPolygon()).ToArray(); this.Perimeter = polys[0]; this.Voids = polys.Skip(1).ToArray(); } }
private void AddPoints(ClipperLib.Clipper clipper, IShape[] shapes, bool[] shouldInclude, ClipperLib.PolyType polyType) { for (var i = 0; i < shapes.Length; i++) { if (shouldInclude[i]) { this.AddPoints(clipper, shapes[i], polyType); } } }
public void ExecuteClip(IClip clip) { BlockSimplification.epsilon = (int64)(simplifyEpsilonPercent / 100f * blockSize * VectorEx.float2int64); List <Vector2i> clipVertices = clip.GetVertices(); ClipBounds bounds = clip.GetBounds(); int x1 = Mathf.Max(0, (int)(bounds.lowerPoint.x / blockSize)); if (x1 > resolutionX - 1) { return; } int y1 = Mathf.Max(0, (int)(bounds.lowerPoint.y / blockSize)); if (y1 > resolutionY - 1) { return; } int x2 = Mathf.Min(resolutionX - 1, (int)(bounds.upperPoint.x / blockSize)); if (x2 < 0) { return; } int y2 = Mathf.Min(resolutionY - 1, (int)(bounds.upperPoint.y / blockSize)); if (y2 < 0) { return; } for (int x = x1; x <= x2; x++) { for (int y = y1; y <= y2; y++) { if (clip.CheckBlockOverlapping(new Vector2f((x + 0.5f) * blockSize, (y + 0.5f) * blockSize), blockSize)) { DestructibleBlock block = blocks[x + resolutionX * y]; List <List <Vector2i> > solutions = new List <List <Vector2i> >(); ClipperLib.Clipper clipper = new ClipperLib.Clipper(); clipper.AddPolygons(block.Polygons, ClipperLib.PolyType.ptSubject); clipper.AddPolygon(clipVertices, ClipperLib.PolyType.ptClip); clipper.Execute(ClipperLib.ClipType.ctDifference, solutions, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero); UpdateBlockBounds(x, y); block.UpdateGeometryWithMoreVertices(solutions, width, height, depth); } } } }
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); }
public void Clip(TurningChisel chisel) { if (polygon == null) { return; } List <Polygon> solution = new List <Polygon>(); ClipperLib.Clipper clipper = new ClipperLib.Clipper(); clipper.AddPolygon(polygon, ClipperLib.PolyType.ptSubject); clipper.AddPolygon(chisel.Polygon, ClipperLib.PolyType.ptClip); clipper.Execute(ClipperLib.ClipType.ctDifference, solution, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero); polygon = null; if (solution.Count > 0) { Vector2i basePosition = transform.localPosition.ToVector2i(); polygon = null; for (int i = 0; i < solution.Count; i++) { var poly = solution[i]; float xA = (float)(poly[1].X - basePosition.X); float xB = (float)(poly[poly.Count - 2].X - basePosition.Y); if (xA * xB < 0f) { polygon = poly; } else { StartCoroutine(CreateDetachedObject(poly)); } } } if (polygon != null) { ExtractEdge(); PostShapeChange(); } else { polygon = null; ExtractEdge(); PostShapeChange(); } }
private Zones.TPolygon[] CalcOutOfBoundsPoly(Zones.TPolygon Tile, Zones.TPolygon BoxRect, ClipperLib.ClipType clipType) { List <List <ClipperLib.IntPoint> > subj = new List <List <ClipperLib.IntPoint> >(); subj.Add(new List <ClipperLib.IntPoint>()); for (int i = 0; i < Tile.Dots.Length; i++) { subj[0].Add(new ClipperLib.IntPoint((long)Tile[i].X, (long)Tile[i].Y)); } List <List <ClipperLib.IntPoint> > clip = new List <List <ClipperLib.IntPoint> >(); clip.Add(new List <ClipperLib.IntPoint>(4)); for (int i = 0; i < BoxRect.Dots.Length; i++) { clip[0].Add(new ClipperLib.IntPoint((long)BoxRect[i].X, (long)BoxRect[i].Y)); } List <ClipperLib.ExPolygon> solution = new List <ClipperLib.ExPolygon>(); ClipperLib.Clipper c = new ClipperLib.Clipper(); c.AddPolygons(clip, ClipperLib.PolyType.ptSubject); c.AddPolygons(subj, ClipperLib.PolyType.ptClip); bool res = (c.Execute(clipType, solution)); // , ClipperLib.PolyFillType.pftEvenOdd, ClipperLib.PolyFillType.pftEvenOdd if (res) { if (solution.Count > 0) { List <Zones.TPolygon> pols = new List <Zones.TPolygon>(); for (int s = 0; s < solution.Count; s++) { Zones.TPolygon poly = new Zones.TPolygon((ushort)solution[s].outer.Count); for (int i = 0; i < poly.Dots.Length; i++) { poly[i] = new Zones.TPoint((double)solution[s].outer[i].X, (double)solution[s].outer[i].Y); } pols.Add(poly); } ; return(pols.ToArray()); } } ; return(null); }
private double ClippedArea() { if (this.Voids == null || this.Voids.Count == 0) { return(this.Perimeter.Area()); } var clipper = new ClipperLib.Clipper(); clipper.AddPath(this.Perimeter.ToClipperPath(), ClipperLib.PolyType.ptSubject, true); clipper.AddPaths(this.Voids.Select(p => p.ToClipperPath()).ToList(), ClipperLib.PolyType.ptClip, true); var solution = new List <List <ClipperLib.IntPoint> >(); clipper.Execute(ClipperLib.ClipType.ctDifference, solution, ClipperLib.PolyFillType.pftEvenOdd); return(solution.Sum(s => ClipperLib.Clipper.Area(s)) / Math.Pow(1024.0, 2)); }
private void AddPoints(ClipperLib.Clipper clipper, IShape shape, ClipperLib.PolyType polyType) { foreach (var path in shape) { var points = path.AsSimpleLinearPath(); var clipperPoints = new List <ClipperLib.IntPoint>(); foreach (var point in points) { var p = point * ClipperScaleFactor; clipperPoints.Add(new ClipperLib.IntPoint((long)p.X, (long)p.Y)); } clipper.AddPath( clipperPoints, polyType, path.IsClosed); } }
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); }
/// <summary> /// Tessellate the Floor. /// </summary> public Mesh Mesh() { var clipper = new ClipperLib.Clipper(); clipper.AddPath(this._profile.Perimeter.ToClipperPath(), ClipperLib.PolyType.ptSubject, true); if (this._profile.Voids != null) { clipper.AddPaths(this._profile.Voids.Select(p => p.ToClipperPath()).ToList(), ClipperLib.PolyType.ptClip, true); } var solution = new List <List <ClipperLib.IntPoint> >(); var result = clipper.Execute(ClipperLib.ClipType.ctDifference, solution, ClipperLib.PolyFillType.pftEvenOdd); var polys = solution.Select(s => s.ToPolygon()).ToList(); if (polys.Count > 1) { return(Hypar.Geometry.Mesh.Extrude(polys.First(), this.ElementType.Thickness, polys.Skip(1).ToList(), true)); } else { return(Hypar.Geometry.Mesh.Extrude(polys.First(), this.ElementType.Thickness, null, true)); } }
/// <summary> /// Generate Colliders based on Tile Collisions /// </summary> /// <param name="isTrigger">True for Trigger Collider, false otherwise</param> /// <param name="zDepth">Z Depth of the collider.</param> /// <param name="colliderWidth">Width of the collider, in Units</param> /// <param name="used2DColider">True to generate a 2D collider, false to generate a 3D collider.</param> /// <param name="innerCollision">If true, calculate normals facing the anchor of the collider (inside collisions), else, outside collisions.</param> /// <returns>A GameObject containing all generated colliders</returns> public GameObject GenerateTileCollisions(bool used2DColider = true, bool isTrigger = false, float zDepth = 0, float colliderWidth = 1, bool innerCollision = false) { GameObject tileColisions = new GameObject("Tile Collisions"); tileColisions.transform.parent = MapObject.transform; tileColisions.transform.localPosition = Vector3.zero; ClipperLib.Clipper clipper = new ClipperLib.Clipper(); List<List<ClipperLib.IntPoint>> pathsList = new List<List<ClipperLib.IntPoint>>(); List<List<ClipperLib.IntPoint>> solution = new List<List<ClipperLib.IntPoint>>(); // Iterate over each Tile Layer, grab all TileObjects inside this layer and use their Paths with ClipperLib to generate one polygon collider foreach (var layer in Layers) { if (layer is TileLayer) { clipper.Clear(); solution.Clear(); pathsList.Clear(); TileLayer tileLayer = layer as TileLayer; for (int x = 0; x < tileLayer.Tiles.Width; x++) { for (int y = 0; y < tileLayer.Tiles.Height; y++) { Tile t = tileLayer.Tiles[x, y]; if (t == null || t.TileSet == null || t.TileSet.TilesObjects == null) continue; if (t.TileSet.TilesObjects.ContainsKey(t.OriginalID)) { List<TileObject> tileObjs = t.TileSet.TilesObjects[t.OriginalID]; foreach (var tileObj in tileObjs) { pathsList.Add(tileObj.GetPath(x, y, t.SpriteEffects, _tileObjectEllipsePrecision)); } } } } // Add the paths to be merged to ClipperLib clipper.AddPaths(pathsList, ClipperLib.PolyType.ptSubject, true); // Merge it! //clipper.PreserveCollinear = false; //clipper.ReverseSolution = true; clipper.StrictlySimple = _simpleTileObjectCalculation; if (!clipper.Execute(ClipperLib.ClipType.ctUnion, solution)) continue; clipper.Execute(ClipperLib.ClipType.ctUnion, solution); // Now solution should contain all vertices of the collision object, but they are still multiplied by TileObject.ClipperScale! #region Implementation of increase and decrease offset polygon. if(_simpleTileObjectCalculation == false) { // Link of the example of ClipperLib: // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/_Body.htm ClipperLib.ClipperOffset co = new ClipperLib.ClipperOffset(_clipperMiterLimit,_clipperArcTolerance); foreach(List<ClipperLib.IntPoint> item in solution) { co.AddPath(item, _clipperJoinType,_clipperEndType); } solution.Clear(); co.Execute(ref solution, _clipperDeltaOffset*TileObject.ClipperScale); } #endregion // Generate this path's collision GameObject newCollider = new GameObject("Tile Collisions " + layer.Name); newCollider.transform.parent = tileColisions.transform; newCollider.transform.localPosition = new Vector3(0, 0, zDepth); // Finally, generate the edge collider int counter = 0; for (int i = 0; i < solution.Count; i++) { if (solution[i].Count < 1) continue; List<Vector2> points = new List<Vector2>(); for (int j = 0; j < solution[i].Count; j++) { points.Add( new Vector2( solution[i][j].X / (float)TileObject.ClipperScale, solution[i][j].Y / (float)TileObject.ClipperScale ) ); } if (used2DColider) Generate2DTileCollision(tileLayer, counter, newCollider.transform, points, isTrigger, zDepth); else Generate3DTileCollision(tileLayer, counter, newCollider.transform, points, isTrigger, zDepth, colliderWidth, innerCollision); counter++; } newCollider.isStatic = true; } } return tileColisions; }
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; }
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); }
public static List<List<Vector2>> GenerateClipperPathPoints(TileLayer tileLayer, bool simpleTileObjectCalculation = true, double clipperArcTolerance = 0.25, double clipperMiterLimit = 2.0, ClipperLib.JoinType clipperJoinType = ClipperLib.JoinType.jtRound, ClipperLib.EndType clipperEndType = ClipperLib.EndType.etClosedPolygon, float clipperDeltaOffset = 0) { ClipperLib.Clipper clipper = new ClipperLib.Clipper(); List<List<ClipperLib.IntPoint>> pathsList = new List<List<ClipperLib.IntPoint>>(); List<List<ClipperLib.IntPoint>> solution = new List<List<ClipperLib.IntPoint>>(); List<List<Vector2>> points = new List<List<Vector2>>(); for (int x = 0; x < tileLayer.Tiles.Width; x++) { for (int y = 0; y < tileLayer.Tiles.Height; y++) { Tile t = tileLayer.Tiles[x, y]; if (t == null || t.TileSet == null || t.TileSet.TilesObjects == null) continue; if (t.TileSet.TilesObjects.ContainsKey(t.OriginalID)) { List<TileObject> tileObjs = t.TileSet.TilesObjects[t.OriginalID]; foreach (var tileObj in tileObjs) { pathsList.Add(tileObj.GetPath(x, y, t.SpriteEffects, tileLayer.BaseMap.MapRenderParameter.TileWidth, tileLayer.BaseMap.MapRenderParameter.TileHeight)); } } } } // Add the paths to be merged to ClipperLib clipper.AddPaths(pathsList, ClipperLib.PolyType.ptSubject, true); // Merge it! //clipper.PreserveCollinear = false; //clipper.ReverseSolution = true; clipper.StrictlySimple = simpleTileObjectCalculation; if (!clipper.Execute(ClipperLib.ClipType.ctUnion, solution)) return points; clipper.Execute(ClipperLib.ClipType.ctUnion, solution); // Now solution should contain all vertices of the collision object, but they are still multiplied by TileObject.ClipperScale! #region Implementation of increase and decrease offset polygon. if (simpleTileObjectCalculation == false) { // Link of the example of ClipperLib: // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/_Body.htm ClipperLib.ClipperOffset co = new ClipperLib.ClipperOffset(clipperMiterLimit, clipperArcTolerance); foreach (List<ClipperLib.IntPoint> item in solution) { co.AddPath(item, clipperJoinType, clipperEndType); } solution.Clear(); co.Execute(ref solution, clipperDeltaOffset * TileObject.ClipperScale); } #endregion for (int i = 0; i < solution.Count; i++) { if (solution[i].Count < 1) continue; points.Add(new List<Vector2>()); for (int j = 0; j < solution[i].Count; j++) { points[i].Add( new Vector2( solution[i][j].X / (float)TileObject.ClipperScale, solution[i][j].Y / (float)TileObject.ClipperScale ) ); } } return points; }
public void CalculatePolygons( double? overrideRelativeStructureWidth, out List<List<ClipperLib.IntPoint>> framePolygon, out List<List<ClipperLib.IntPoint>> insetPolygon, out List<List<ClipperLib.IntPoint>> fillPolygon) { insetPolygon = null; framePolygon = null; fillPolygon = null; // get outer polygon var outerPolygon = GetCopyOfOuterPolygon(); List<List<ClipperLib.IntPoint>> innerFramePolygon = null; double relativeStructureWidth = overrideRelativeStructureWidth ?? _relativeStructureWidth; if (null != _frame && relativeStructureWidth > 0) { // get frame polygon innerFramePolygon = _frame.GetCopyOfClipperPolygon(relativeStructureWidth, outerPolygon); } if (null != _inset) { // get inset polygon insetPolygon = _inset.GetCopyOfClipperPolygon(relativeStructureWidth); } // if null != insetPolygon // clip with innerPolygon ?? outerPolygon; // store clipped inset polygon / draw it with inset color if (null != insetPolygon) { var clipper = new ClipperLib.Clipper(); var solution = new List<List<ClipperLib.IntPoint>>(); clipper.AddPaths(insetPolygon, ClipperLib.PolyType.ptSubject, true); clipper.AddPaths(innerFramePolygon ?? outerPolygon, ClipperLib.PolyType.ptClip, true); clipper.Execute(ClipperLib.ClipType.ctIntersection, solution); insetPolygon = solution; } // if null != framePolygon // clip with outer polygon ???? // draw combined path of outer polygon and frame polygon as a hole with frame color if (null != innerFramePolygon) { var clipper = new ClipperLib.Clipper(); clipper.AddPaths(outerPolygon, ClipperLib.PolyType.ptSubject, true); clipper.AddPaths(innerFramePolygon, ClipperLib.PolyType.ptClip, true); framePolygon = new List<List<ClipperLib.IntPoint>>(); clipper.Execute(ClipperLib.ClipType.ctDifference, framePolygon); } // calculate // if null != insetPolygon // (framePolygon ?? outerPolygon ) - insetPolygon // or else use (framePolygon ?? outerPolygon ) directly // draw result with fillColor if (null != insetPolygon) { var clipper = new ClipperLib.Clipper(); clipper.AddPaths(innerFramePolygon ?? outerPolygon, ClipperLib.PolyType.ptSubject, true); clipper.AddPaths(insetPolygon, ClipperLib.PolyType.ptClip, true); fillPolygon = new List<List<ClipperLib.IntPoint>>(); clipper.Execute(ClipperLib.ClipType.ctDifference, fillPolygon); } else { fillPolygon = innerFramePolygon ?? outerPolygon; } }
public void CalculatePolygons( double?overrideRelativeStructureWidth, out List <List <ClipperLib.IntPoint> > framePolygon, out List <List <ClipperLib.IntPoint> > insetPolygon, out List <List <ClipperLib.IntPoint> > fillPolygon) { insetPolygon = null; framePolygon = null; fillPolygon = null; // get outer polygon var outerPolygon = GetCopyOfOuterPolygon(); List <List <ClipperLib.IntPoint> > innerFramePolygon = null; double relativeStructureWidth = overrideRelativeStructureWidth ?? _relativeStructureWidth; if (null != _frame && relativeStructureWidth > 0) { // get frame polygon innerFramePolygon = _frame.GetCopyOfClipperPolygon(relativeStructureWidth, outerPolygon); } if (null != _inset) { // get inset polygon insetPolygon = _inset.GetCopyOfClipperPolygon(relativeStructureWidth); } // if null != insetPolygon // clip with innerPolygon ?? outerPolygon; // store clipped inset polygon / draw it with inset color if (null != insetPolygon) { var clipper = new ClipperLib.Clipper(); var solution = new List <List <ClipperLib.IntPoint> >(); clipper.AddPaths(insetPolygon, ClipperLib.PolyType.ptSubject, true); clipper.AddPaths(innerFramePolygon ?? outerPolygon, ClipperLib.PolyType.ptClip, true); clipper.Execute(ClipperLib.ClipType.ctIntersection, solution); insetPolygon = solution; } // if null != framePolygon // clip with outer polygon ???? // draw combined path of outer polygon and frame polygon as a hole with frame color if (null != innerFramePolygon) { var clipper = new ClipperLib.Clipper(); clipper.AddPaths(outerPolygon, ClipperLib.PolyType.ptSubject, true); clipper.AddPaths(innerFramePolygon, ClipperLib.PolyType.ptClip, true); framePolygon = new List <List <ClipperLib.IntPoint> >(); clipper.Execute(ClipperLib.ClipType.ctDifference, framePolygon); } // calculate // if null != insetPolygon // (framePolygon ?? outerPolygon ) - insetPolygon // or else use (framePolygon ?? outerPolygon ) directly // draw result with fillColor if (null != insetPolygon) { var clipper = new ClipperLib.Clipper(); clipper.AddPaths(innerFramePolygon ?? outerPolygon, ClipperLib.PolyType.ptSubject, true); clipper.AddPaths(insetPolygon, ClipperLib.PolyType.ptClip, true); fillPolygon = new List <List <ClipperLib.IntPoint> >(); clipper.Execute(ClipperLib.ClipType.ctDifference, fillPolygon); } else { fillPolygon = innerFramePolygon ?? outerPolygon; } }
public static Mesh P2TTriangulate(TTFTextOutline outline, Mesh outMesh) { float Z = outline.Min.z; // Assume all vertices have the same Z coord List <List <ClipperLib.IntPoint> > ipolygones = new List <List <ClipperLib.IntPoint> >(); foreach (TTFTextOutline.Boundary lv in outline.boundaries) { if (Intersection.isSimple(lv)) { ipolygones.Add(Vec2LIP(lv)); } else { //Debug.LogWarning("Complex Contour"); try { foreach (List <Vector3> p in Intersection.Decompose(lv)) // decompose outlines { ipolygones.Add(Vec2LIP(p)); } } catch (System.Exception) { // parallel segments //Debug.LogWarning("Bad contour:" + ex.ToString()); } } } ClipperLib.Clipper c = new ClipperLib.Clipper(); List <ClipperLib.ExPolygon> res = new List <ClipperLib.ExPolygon>(); c.AddPolygons(ipolygones, ClipperLib.PolyType.ptSubject); c.Execute(ClipperLib.ClipType.ctUnion, res, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero); //c.Execute(ClipperLib.ClipType.ctUnion,res,ClipperLib.PolyFillType.pftEvenOdd,ClipperLib.PolyFillType.pftEvenOdd); //c.Execute(ClipperLib.ClipType.ctUnion,res,ClipperLib.PolyFillType.pftPositive,ClipperLib.PolyFillType.pftPositive); //c.Execute(ClipperLib.ClipType.ctUnion,res,ClipperLib.PolyFillType.pftPositive,ClipperLib.PolyFillType.pftPositive); List <Vector3> vertices = new List <Vector3>(); List <int> triangles = new List <int>(); Dictionary <Vector3, int> vDic = new Dictionary <Vector3, int>(); foreach (ClipperLib.ExPolygon p in res) { Vector3 mv = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); Vector3 Mv = new Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity); List <Vector3> np = LIP2Vec(p.outer); for (int i = 0; i < np.Count; i++) { mv = Vector3.Min(mv, np[i]); Mv = Vector3.Max(Mv, np[i]); } Vector3 sv = (Mv - mv); //int oc=np.Count; np = Simplify(np, 0.05f); // set of all vertices for this triangulation step HashSet <Vector3> whole = new HashSet <Vector3>(); if ((np.Count >= 4) && ((sv.x * sv.y) > 0.001f) && Intersection.isSimple(np) && !HasDup(np)) { foreach (Vector3 v in np) { whole.Add(v); } Polygon cp = new Polygon(Vec2PP(np)); foreach (List <ClipperLib.IntPoint> h in p.holes) { List <Vector3> ph = Simplify(LIP2Vec(h), 0.05f); // Poli2tri doesnt support intersection and dupplicate vertices // skip all holes which does not respect thoses conditions if ((!Intersection.isSimple(ph)) || HasDup(ph)) { //Debug.LogWarning("Skipping Complex hole"); } else if (ContainsAny(whole, ph)) { //Debug.LogWarning("Duplicate vertices in holes"); } else { foreach (Vector3 v in ph) { whole.Add(v); } cp.AddHole(new Polygon(Vec2PP(ph))); } } try { P2T.Triangulate(cp); foreach (DelaunayTriangle dt in cp.Triangles) { Vector3 v; int idx; for (int i = 0; i < 3; ++i) { v = new Vector3(dt.Points[i].Xf, dt.Points[i].Yf, Z); if (!vDic.TryGetValue(v, out idx)) { idx = vertices.Count; vertices.Add(v); vDic.Add(v, idx); } triangles.Add(idx); } } } catch (System.Exception e) { Debug.LogWarning(e); } } else { //Debug.LogWarning("Skip Polygon: count=" + np.Count + " size=" + sv + " simple=" + Intersection.isSimple(np) + " dup:" + HasDup(np)); } } outMesh.Clear(); outMesh.vertices = vertices.ToArray(); outMesh.triangles = triangles.ToArray(); outMesh.RecalculateBounds(); outMesh.RecalculateNormals(); outMesh.Optimize(); return(outMesh); }
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); }
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; }
public override List <List <Polygon> > GetPolygons() { // if (FillEnabled == false) return new List<List<Polygon>>(); List <Polygon> CombinedPolygons = new List <Polygon>(); Polygons solution = new Polygons(); ClipperLib.Clipper cp = new ClipperLib.Clipper(); int first = 0; foreach (var p in TransformedPolygons) { Polygons clips = new Polygons(); List <ClipperLib.IntPoint> clipperpoly = new List <ClipperLib.IntPoint>(); foreach (var v in p.Vertices) { clipperpoly.Add(new ClipperLib.IntPoint((long)(v.x * 100000), (long)(v.y * 100000))); } clips.Add(clipperpoly); // cp.AddPolygons(clips, (first != 0)?PolyType.ptClip:PolyType.ptSubject); cp.AddPolygons(clips, ClipperLib.PolyType.ptSubject); first++; } cp.Execute(ClipperLib.ClipType.ctUnion, solution, FillRule, FillRule); foreach (var l in solution) { Polygon p = new Polygon("SVGPathCollection"); foreach (var v in l) { p.Vertices.Add(new vec2(v.X / 100000.0, v.Y / 100000.0)); } p.r = FillColor.R; p.g = FillColor.G; p.b = FillColor.B; // p.a = (byte)(FillColor.A/3); // if (FillColor != Color.White) p.a = 0; p.a = FillColor.A; p.Filled = FillEnabled; p.Stroke = StrokeEnabled; p.StrokeWidth = StrokeWidthInMM; p.strokeR = StrokeColor.R; p.strokeG = StrokeColor.G; p.strokeB = StrokeColor.B; p.strokeA = StrokeColor.A; CombinedPolygons.Add(p); } List <List <Polygon> > P = new List <List <Polygon> >() { CombinedPolygons }; return(P); }
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; }