/// <summary> /// Find cut lines that separates all point sets equally. /// Generates dcel of dual lines and generates cut lines through intersection of middle faces. /// </summary> /// <param name="a_points1"></param> /// <param name="a_points2"></param> /// <param name="a_points3"></param> /// <returns></returns> public static List <Line> FindCutLines(IEnumerable <Vector2> a_points1, IEnumerable <Vector2> a_points2, IEnumerable <Vector2> a_points3) { // obtain dual lines for game objects var lines1 = PointLineDual.Dual(a_points1); var lines2 = PointLineDual.Dual(a_points2); var lines3 = PointLineDual.Dual(a_points3); // add lines together var allLines = lines1.Concat(lines2.Concat(lines3)); // calculate bounding box around line intersections with some margin var bBox = BoundingBoxComputer.FromLines(allLines, 10f); // calculate dcel for line inside given bounding box var dcel1 = new DCEL(lines1, bBox); var dcel2 = new DCEL(lines2, bBox); var dcel3 = new DCEL(lines3, bBox); // find faces in the middle of the lines vertically var archerFaces = MiddleFaces(dcel1, lines1); var swordsmenFaces = MiddleFaces(dcel2, lines2); var mageFaces = MiddleFaces(dcel3, lines3); // obtain cut lines for the dcel middle faces return(FindCutlinesInDual(archerFaces, swordsmenFaces, mageFaces)); }
/// <summary> /// Finds a number of solutions for the cut problem. Both per type of soldier and for all soldiers. /// /// NOTE: only works if the x coords of all things are all positive or all negative /// </summary> public void FindSolution() { // obtain dual lines for game objects m_archerLines = PointLineDual.Dual(m_archers.Select(x => (Vector2)x.transform.position)).ToList(); m_spearmenLines = PointLineDual.Dual(m_spearmen.Select(x => (Vector2)x.transform.position)).ToList(); m_mageLines = PointLineDual.Dual(m_mages.Select(x => (Vector2)x.transform.position)).ToList(); // add lines together var allLines = m_archerLines.Concat(m_spearmenLines.Concat(m_mageLines)); // calculate bounding box around line intersections with some margin var bBox = BoundingBoxComputer.FromLines(allLines, 10f); // calculate dcel for line inside given bounding box m_archerDcel = new DCEL(m_archerLines, bBox); m_spearmenDcel = new DCEL(m_spearmenLines, bBox); m_mageDcel = new DCEL(m_mageLines, bBox); // find faces in the middle of the lines vertically m_archerFaces = HamSandwich.MiddleFaces(m_archerDcel, m_archerLines); m_spearmenFaces = HamSandwich.MiddleFaces(m_spearmenDcel, m_spearmenLines); m_mageFaces = HamSandwich.MiddleFaces(m_mageDcel, m_mageLines); // obtain cut lines for the dcel middle faces and final possible cutlines m_solution = new DivideSolution(HamSandwich.FindCutlinesInDual(m_archerFaces), HamSandwich.FindCutlinesInDual(m_spearmenFaces), HamSandwich.FindCutlinesInDual(m_mageFaces), HamSandwich.FindCutlinesInDual(m_archerFaces, m_spearmenFaces, m_mageFaces)); // update solution to the drawer m_lineDrawer.Solution = m_solution; }
// Random endless level generation // Determines the amount of shepherds placed based on the current level number private ShepherdLevel CreateEndlessLevel(int level) { // create the output scriptable object var asset = ScriptableObject.CreateInstance <ShepherdLevel>(); // place the shepherds and sheep randomly List <Vector2> shepherds = RandomPos(level + 4); List <Vector2> sheep = RandomPos(2 * (level + 4)); // Print locations string sls = "Shepherd locations: \n"; foreach (Vector2 v in shepherds) { sls += "(" + v.x + ", " + v.y + "), "; } Debug.Log(sls); string shls = "Sheep locations: \n"; foreach (Vector2 v in sheep) { shls += "(" + v.x + ", " + v.y + "), "; } Debug.Log(shls); // Construct the voronoi diagram corresponding to the shepherd locations StartVoronoi(); foreach (Vector2 me in shepherds) { // Add vertex to the triangulation and update the voronoi Delaunay.AddVertex(m_delaunay, me); m_delaunay.SetOwner(me, Random.Range(0, 4)); m_dcel = Voronoi.Create(m_delaunay); } // Create vertical decomposition VerticalDecomposition vd = VertDecomp(m_dcel); // Use the vertical decomposition to determine the ownership of each sheep // and add the sheep to the level foreach (Vector2 s in sheep) { Trapezoid trap = vd.Search(s); Face area = trap.bottom.face; int i = area.owner; asset.addSheep(s, i); } // Normalize coordinates var rect = BoundingBoxComputer.FromPoints(asset.SheepList); asset.SheepList = Normalize(rect, 6f, asset.SheepList); // Set shepherd budget asset.setBudget(shepherds.Count); return(asset); }
/// <summary> /// Computes bounding rectangle around the face /// </summary> /// <param name="margin"></param> /// <returns></returns> public Rect BoundingBox(float margin = 0f) { if (IsOuter) { throw new GeomException("Bounding box is ill-defined for outer face"); } return(BoundingBoxComputer.FromPoints(OuterVertices.Select(x => x.Pos), margin)); }
public void FromVector2Test() { var ret = BoundingBoxComputer.FromPoints(m_points); Assert.IsTrue(CmpRect(expRect, ret)); ret = BoundingBoxComputer.FromPoints(m_points, 1f); Assert.IsTrue(CmpRect(expRectMargin, ret)); Assert.AreEqual(BoundingBoxComputer.FromPoints(new List <Vector2>()), new Rect()); }
public void FromLinesTest() { var ret = BoundingBoxComputer.FromLines(m_lines); Assert.IsTrue(CmpRect(expRect, ret)); ret = BoundingBoxComputer.FromLines(m_lines, 1f); Assert.IsTrue(CmpRect(expRectMargin, ret)); Assert.AreEqual(BoundingBoxComputer.FromLines(new List <Line>()), new Rect()); }
public HamSandwichTest() { m_points = new List <Vector2>() { new Vector2(1, -2), new Vector2(2, 0), new Vector2(5, 5), new Vector2(-3, -1) }; m_lines = new List <Line>() { new Line(1, 2), new Line(2, 0), new Line(5, -5), new Line(-3, 1) }; m_rect = BoundingBoxComputer.FromLines(m_lines, 10f); m_dcel = new DCEL(m_lines, m_rect); m_points1 = new List <Vector2>() { new Vector2(-1, 1), new Vector2(2, -1) }; m_points2 = new List <Vector2>() { new Vector2(3, 2), new Vector2(3.5f, 0) }; m_points3 = new List <Vector2>() { new Vector2(-3, 1), new Vector2(-3.4f, -3) }; m_lines1 = new List <Line>() { new Line(-1, -1), new Line(2, 1) }; m_lines2 = new List <Line>() { new Line(3, -2), new Line(3.5f, 0) }; m_lines3 = new List <Line>() { new Line(-3, -1), new Line(-3.4f, 3) }; m_allLines = m_lines1.Concat(m_lines2.Concat(m_lines3)).ToList(); m_rect = BoundingBoxComputer.FromLines(m_allLines, 10f); m_dcel1 = new DCEL(m_lines1, m_rect); m_dcel2 = new DCEL(m_lines2, m_rect); m_dcel3 = new DCEL(m_lines3, m_rect); }
private UnityEngine.Object LoadKingsTaxesLevel(XElement fileSelected, string name) { // create the output scriptable object var asset = ScriptableObject.CreateInstance <KingsTaxesLevel>(); // retrieve page data from .ipe file var items = fileSelected.Descendants("page").First().Descendants("use"); // get marker data into respective vector list asset.Villages.AddRange(GetMarkers(items, "disk")); asset.Castles.AddRange(GetMarkers(items, "square")); // normalize coordinates var total = new List <Vector2>(); total.AddRange(asset.Villages); total.AddRange(asset.Castles); var rect = BoundingBoxComputer.FromPoints(total); asset.Villages = Normalize(rect, ktSIZE, asset.Villages); asset.Castles = Normalize(rect, ktSIZE, asset.Castles); // give warning if no relevant data found if (asset.Villages.Count + asset.Castles.Count == 0) { EditorUtility.DisplayDialog("Warning", "File does not contain any villages/castles (disks and/or squares).", "OK"); } // get level arguments var args = name.Split('_').ToList(); if (args.Count > 2) { foreach (var item in args) { Debug.Log(item); } EditorUtility.DisplayDialog("Error", "Too many level arguments given in path name.", "OK"); return(asset); } else if (args.Count == 2) { if (!float.TryParse(args[1], out asset.TSpannerRatio)) { EditorUtility.DisplayDialog("Error", "Could not parse the t-spanner ratio.", "OK"); return(asset); } } return(asset); }
public static List <Line> FindCutLines(IEnumerable <Vector2> a_points) { // obtain dual lines for points var lines = PointLineDual.Dual(a_points); // calculate bounding box around line intersections with some margin var bBox = BoundingBoxComputer.FromLines(lines, 10f); // calculate dcel for line inside given bounding box var m_dcel = new DCEL(lines, bBox); // find faces in the middle of the lines vertically and calculate cut lines var faces = MiddleFaces(m_dcel, lines); return(FindCutlinesInDual(faces)); }
/// <summary> /// Creates a Unity mesh object from this triangulation /// Mapping between Unity mesh triangulation and this custom triangulation class. /// </summary> /// <returns></returns> public Mesh CreateMesh() { Mesh mesh = new Mesh(); // make indexed map of vertices var vertices = new Dictionary <Vector2, int>(); var index = 0; foreach (var t in m_Triangles) { foreach (var v in t.Vertices) { if (!vertices.ContainsKey(v)) { vertices.Add(v, index++); } } } var vertexList = vertices.Keys.ToList(); // Calculate UV's var bbox = BoundingBoxComputer.FromPoints(vertexList, 0.1f); var newUV = vertexList.Select <Vector2, Vector2>(p => Rect.PointToNormalized(bbox, p)); // Calculate mesh triangles var tri = new List <int>(); foreach (var t in m_Triangles) { tri.AddRange(new int[3] { vertices[t.P0], vertices[t.P1], vertices[t.P2] }); } // set mesh variables mesh.vertices = vertexList.Select <Vector2, Vector3>(p => p).ToArray(); mesh.uv = newUV.ToArray(); mesh.triangles = tri.ToArray(); return(mesh); }
public TrapezoidalDecomposition(IEnumerable <CountryLineSegment> a_segments) { Segments = new List <CountryLineSegment>(a_segments.ToArray()); BoundingBox = RectToTrapezoid(BoundingBoxComputer.FromSegments(a_segments.Select(e => e.Segment).ToList())); TrapezoidalMap = new TrapezoidalMap(BoundingBox); SearchGraph = new SearchGraph(BoundingBox); Segments.Shuffle(); foreach (CountryLineSegment seg in Segments) { List <Node> oldTrapezoids = FollowSegment(seg); foreach (Node n in oldTrapezoids) { TrapezoidalMap.Trapezoids.Remove((Trapezoid)n.Value); } List <Trapezoid> newTrapezoids = SearchGraph.Update(ref oldTrapezoids, seg); TrapezoidalMap.AddTrapezoids(newTrapezoids); } }
private UnityEngine.Object LoadShepherdLevel(XElement fileSelected, string name) { // create the output scriptable object var asset = ScriptableObject.CreateInstance <ShepherdLevel>(); // retrieve page data from .ipe file var items = fileSelected.Descendants("page").First().Descendants("use"); // get marker data into respective vector list List <string> markerTypes = new List <string> { "disk", "square", "cross", "circle" }; for (int i = 0; i < markerTypes.Count; i++) { List <Vector2> sheepLocs = GetMarkers(items, markerTypes[i]); foreach (Vector2 l in sheepLocs) { asset.addSheep(l, i); } } // normalize coordinates var rect = BoundingBoxComputer.FromPoints(asset.SheepList); asset.SheepList = Normalize(rect, shSIZE, asset.SheepList); // give warning if no relevant data found if (asset.SheepList.Count == 0) { EditorUtility.DisplayDialog("Warning", "File does not contain any valid markers.", "OK"); } asset.setBudget(int.Parse(name.Split('_').Last())); return(asset); }
/// <summary> /// Loads a convex hull level. /// </summary> /// <param name="fileSelected"></param> /// <param name="name"></param> /// <returns></returns> private UnityEngine.Object LoadHullLevel(XElement fileSelected, string name) { // create the output scriptable object var asset = ScriptableObject.CreateInstance<HullLevel>(); // retrieve page data from .ipe file var items = fileSelected.Descendants("page").First().Descendants("use"); // get marker data into respective vector list asset.Points.AddRange(GetMarkers(items, "disk")); // normalize coordinates var rect = BoundingBoxComputer.FromPoints(asset.Points); asset.Points = Normalize(rect, ktSIZE, asset.Points); // give warning if no relevant data found if (asset.Points.Count == 0) { EditorUtility.DisplayDialog("Warning", "File does not contain any villages/castles (disks and/or squares).", "OK"); } return asset; }
public Object LoadArtGalleryLevel(XElement fileSelected, string name) { // create the output scriptable object var asset = ScriptableObject.CreateInstance <ArtGalleryLevel>(); // retrieve page data from .ipe file var items = fileSelected.Descendants("page").First().Descendants("path").ToList(); // check that .ipe file contains one and only one polygon if (items.Count == 0) { EditorUtility.DisplayDialog("Error", "No paths (lines/polygons) found in ipe file.", "OK"); return(asset); } else if (items.Count > 1) { EditorUtility.DisplayDialog("Error", "File contains too many paths (lines/polygons).", "OK"); return(asset); } // retrieve coordinates from .ipe file var points = new List <Vector2>(); foreach (var item in items[0].Value.Split('\n')) { var coords = item.Split(' ').ToList(); if (coords.Count < 2) { continue; } var x = float.Parse(coords[0]); var y = float.Parse(coords[1]); if (!MathUtil.IsFinite(x) || !MathUtil.IsFinite(y)) { continue; } points.Add(new Vector2(x, y)); } // normalize coordinates var rect = BoundingBoxComputer.FromPoints(points); Normalize(rect, agSIZE, ref points); asset.Outer = points; // reverse if not clockwise if (!asset.Polygon.IsClockwise()) { points.Reverse(); asset.Outer = points; } // get level arguments var args = name.Split('_').ToList(); if (args.Count > 2) { EditorUtility.DisplayDialog("Error", "Too many level arguments given in path name", "OK"); return(asset); } else if (args.Count == 2) { asset.MaxNumberOfLighthouses = int.Parse(args[1]); } return(asset); }
public UnityEngine.Object LoadArtGalleryLevel(XElement fileSelected, string name) { // create the output scriptable object var asset = ScriptableObject.CreateInstance <ArtGalleryLevel>(); // retrieve page data from .ipe file var items = fileSelected.Descendants("page").First().Descendants("path").ToList(); // check that .ipe file contains one and only one polygon if (items.Count == 0) { EditorUtility.DisplayDialog("Error", "No paths (lines/polygons) found in ipe file.", "OK"); return(asset); } var outerPoints = new List <Vector2>(); var holes = new List <List <Vector2> >(); var checkPoints = new List <Vector2>(); foreach (var poly in items) { List <float> transformation = null; if (poly.Attribute("matrix") != null) { transformation = poly.Attribute("matrix").Value .Split(' ') .Select(s => float.Parse(s)) .ToList(); } // retrieve coordinates from .ipe file var points = new List <Vector2>(); foreach (var coordString in poly.Value.Split('\n')) { var coords = coordString.Split(' ').ToList(); if (coords.Count < 2) { continue; } var x = float.Parse(coords[0]); var y = float.Parse(coords[1]); if (!MathUtil.IsFinite(x) || !MathUtil.IsFinite(y)) { continue; } if (transformation != null) { // apply transformation matrix (could be made into library function) x = transformation[0] * x + transformation[2] * y + transformation[4]; y = transformation[1] * x + transformation[3] * y + transformation[5]; } points.Add(new Vector2(x, y)); } if (outerPoints.Count == 0 || new Polygon2D(outerPoints).Area < new Polygon2D(points).Area) { if (outerPoints.Count > 0) { holes.Add(outerPoints); } outerPoints = points; } else { holes.Add(points); } // Add all defining vertices to checkPoints } // normalize coordinates var rect = BoundingBoxComputer.FromPoints(outerPoints); outerPoints = Normalize(rect, agSIZE, outerPoints); checkPoints.AddRange(outerPoints); for (var i = 0; i < holes.Count; i++) { holes[i] = Normalize(rect, agSIZE, holes[i]); checkPoints.AddRange(holes[i]); } // reverse if not clockwise if (!(new Polygon2D(outerPoints).IsClockwise())) { outerPoints.Reverse(); } for (var i = 0; i < holes.Count; i++) { // reverse if not clockwise if (!(new Polygon2D(holes[i]).IsClockwise())) { holes[i].Reverse(); } } var gridPoints = ComputeGridPoints(rect, outerPoints, holes, 50); checkPoints.AddRange(gridPoints); asset.Outer = outerPoints; asset.Holes = holes.Select(h => new Vector2Array(h.ToArray())).ToList(); asset.CheckPoints = checkPoints; Debug.Log(asset.CheckPoints); // get level arguments var args = name.Split('_').ToList(); if (args.Count > 2) { EditorUtility.DisplayDialog("Error", "Too many level arguments given in path name", "OK"); return(asset); } else if (args.Count == 2) { asset.MaxNumberOfLighthouses = int.Parse(args[1]); } return(asset); }
private UnityEngine.Object LoadDivideLevel(XElement fileSelected, string name) { // create the output scriptable object var asset = ScriptableObject.CreateInstance <DivideLevel>(); // retrieve page data from .ipe file var items = fileSelected.Descendants("page").First().Descendants("use"); // get marker data into respective vector list asset.Spearmen.AddRange(GetMarkers(items, "disk")); asset.Archers.AddRange(GetMarkers(items, "square")); asset.Mages.AddRange(GetMarkers(items, "cross")); // normalize coordinates var total = new List <Vector2>(); total.AddRange(asset.Spearmen); total.AddRange(asset.Archers); total.AddRange(asset.Mages); var rect = BoundingBoxComputer.FromPoints(total); asset.Spearmen = Normalize(rect, divSIZE, asset.Spearmen); asset.Archers = Normalize(rect, divSIZE, asset.Archers); asset.Mages = Normalize(rect, divSIZE, asset.Mages); // give warning if no relevant data found if (asset.Spearmen.Count + asset.Archers.Count + asset.Mages.Count == 0) { EditorUtility.DisplayDialog("Warning", "File does not contain any spearmen, archers, mages (disks, squares and/or crosses).", "OK"); } // check for even number of points if (asset.Spearmen.Count % 2 != 0) { EditorUtility.DisplayDialog("Error", "File contains uneven number of spearmen (disks).", "OK"); return(asset); } if (asset.Archers.Count % 2 != 0) { EditorUtility.DisplayDialog("Error", "File contains uneven number of archers (squares).", "OK"); return(asset); } if (asset.Mages.Count % 2 != 0) { EditorUtility.DisplayDialog("Error", "File contains uneven number of mages (crosses).", "OK"); return(asset); } // get level arguments var args = name.Split('_').ToList(); if (args.Count > 2) { EditorUtility.DisplayDialog("Error", "Too many level arguments given in path name.", "OK"); return(asset); } else if (args.Count == 2) { if (!int.TryParse(args[1], out asset.NumberOfSwaps)) { EditorUtility.DisplayDialog("Error", "Could not parse level argument number of swaps.", "OK"); return(asset); } } return(asset); }
public UnityEngine.Object LoadCastleCrushersLevel(XElement fileSelected, string name) { // create the output scriptable object var asset = ScriptableObject.CreateInstance <LinesLevel>(); var points = new List <Vector2>(); // retrieve page data from .ipe file var items = fileSelected.Descendants("page").First().Descendants("path").ToList(); foreach (var line in items) { List <float> transformation = null; if (line.Attribute("matrix") != null) { transformation = line.Attribute("matrix").Value .Split(' ') .Select(s => float.Parse(s)) .ToList(); } foreach (var coordString in line.Value.Split('\n')) { var coords = coordString.Split(' ').ToList(); if (coords.Count < 2) { continue; } var x = float.Parse(coords[0]); var y = float.Parse(coords[1]); if (!MathUtil.IsFinite(x) || !MathUtil.IsFinite(y)) { continue; } if (transformation != null) { // apply transformation matrix (could be made into library function) x = transformation[0] * x + transformation[2] * y + transformation[4]; y = transformation[1] * x + transformation[3] * y + transformation[5]; } points.Add(new Vector2(x, y)); } } // normalize coordinates var rect = BoundingBoxComputer.FromPoints(points); var height = 7f; var width = 11f; if (rect.width <= rect.height) { points = Normalize(rect, height, points); } else { points = Normalize(rect, width, points); rect = BoundingBoxComputer.FromPoints(points); if (rect.height > height) { points = Normalize(rect, height * rect.width / rect.height, points); } } //for (int i = 0; i < points.Count; i++) { // points[i] = new Vector2(((points[i].x - rect.xMin) / rect.width - 0.5f) * 15.84f, // ((points[i].y - rect.yMin) / rect.height - 0.5f) * 10 * 0.75f + 0.75f); //} var first = new List <Vector2>(); var second = new List <Vector2>(); for (int i = 0; i < points.Count;) { first.Add(points[i++]); second.Add(points[i++]); } asset.startPoints = first; asset.endPoints = second; // get level arguments var args = name.Split('_').ToList(); if (args.Count > 2) { EditorUtility.DisplayDialog("Error", "Too many level arguments given in path name", "OK"); return(asset); } else if (args.Count == 2) { asset.maxShots = int.Parse(args[1]); } Debug.Log(args[0] + ": " + asset.endPoints.Count + " walls and " + asset.maxShots + " shots"); return(asset); }
public Rect BoundingBox(float margin = 0f) { return(BoundingBoxComputer.FromPoints(Vertices, margin)); }