/// <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; }
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 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()); }
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()); }