/// <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)); }
public void DualOfDualTest() { var l = new Line(0.5f, -5.2f); var l2 = PointLineDual.Dual(PointLineDual.Dual(l)); Assert.AreEqual(l, l2); }
/// <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; }
public void PointToLineTest() { var v1 = new Vector2(0.5f, 1.2f); var v2 = new Vector2(-0.2f, -9999.1f); var ret1 = PointLineDual.Dual(v1); var ret2 = PointLineDual.Dual(v2); Assert.AreEqual(new Line(0.5f, -1.2f), ret1); Assert.AreEqual(new Line(-0.2f, 9999.1f), ret2); }
public void PointCollectionsTest() { var v1 = new Vector2(0.5f, 1.2f); var v2 = new Vector2(-0.2f, -9999.1f); var ret = PointLineDual.Dual(new List <Vector2>() { v1, v2 }).ToList(); Assert.AreEqual(2, ret.Count); Assert.AreEqual(new Line(0.5f, -1.2f), ret[0]); Assert.AreEqual(new Line(-0.2f, 9999.1f), ret[1]); }
public void LineCollectionsTest() { var l1 = new Line(0.5f, 1.2f); var l2 = new Line(-0.2f, -9999.1f); var ret = PointLineDual.Dual(new List <Line>() { l1, l2 }).ToList(); Assert.AreEqual(2, ret.Count); Assert.IsTrue(MathUtil.EqualsEps(new Vector2(0.5f, -1.2f), ret[0])); Assert.IsTrue(MathUtil.EqualsEps(new Vector2(-0.2f, 9999.1f), ret[1])); }
public void LineToPointTest() { var l1 = new Line(0.5f, 1.2f); var l2 = new Line(-0.2f, -9999.1f); var l3 = new Line(new Vector2(0f, 1.2f), new Vector2(1f, 1.7f)); var ret1 = PointLineDual.Dual(l1); var ret2 = PointLineDual.Dual(l2); var ret3 = PointLineDual.Dual(l3); Assert.IsTrue(MathUtil.EqualsEps(new Vector2(0.5f, -1.2f), ret1)); Assert.IsTrue(MathUtil.EqualsEps(new Vector2(-0.2f, 9999.1f), ret2)); Assert.IsTrue(MathUtil.EqualsEps(ret1, ret3)); }
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> /// Return the Line that gives the greatest distance from all points in the dual. We calculate true seperation and not vertical seperation /// No error checking occurs for bounding box faces/polygons /// </summary> /// <returns>Line with greatest distance to the points encoded by the polygon boundary </returns> public static LineSeparation LineOfGreatestMinimumSeparationInTheDual(Polygon2D polygon, bool a_isBoundingBoxFace) { // copy for safety var poly = new Polygon2D(polygon.Vertices); if (!poly.IsConvex()) { throw new GeomException("Only support computing lines of greatest seperation for convex polygons"); } if (!poly.IsClockwise()) { poly = new Polygon2D(poly.Vertices.Reverse()); } var upperhull = ConvexHull.ComputeUpperHull(poly).ToList(); var lowerhull = ConvexHull.ComputeLowerHull(poly).ToList(); if (a_isBoundingBoxFace) { //Check if the boundingboxface has only 2 real neighbouring lines(in the dual) and return the bisector (in the primal) in this case var reallines = poly.Segments .Select(seg => seg.Line) .Where(line => !line.IsHorizontal && !line.IsVertical) .ToList(); if (reallines.Count < 2) { throw new GeomException("Found impossibly low amount of real lines"); } else if (reallines.Count == 2) { //The two reallines are two points in the primal plane // get intersection // Assumes general positions of initial points var averagepoint = (PointLineDual.Dual(reallines[0]) + PointLineDual.Dual(reallines[1])) / 2; var lineTroughBothPoints = PointLineDual.Dual(reallines[0].Intersect(reallines[1]).Value); var perpLineSlope = -1 / lineTroughBothPoints.Slope; var perpPoint = averagepoint + Vector2.right + Vector2.up * perpLineSlope; return(new LineSeparation(new Line(averagepoint, perpPoint), 0)); //we choose separtion 0 because a line with three constraints in the outer boundingboxface is more important? //TODO this seems untrue, explictly calculate seperation (Wrt to all soldier??) } //Otherwise we return the regular bisector } //zero is the starting corner, 1 is the first interesting point var upperhullIterator = 0; var lowerhullIterator = 0; var candidatePoint = new Vector2(0, 0); //dummy value var currentSeparation = 0f; float currentx = upperhull[0].x; float nextx; float currentheight = 0; float nextheight; LineSegment upperSegment = null; LineSegment lowerSegment = null; Action <float, float> testCandidatePoint = delegate(float testx, float testheight) { var trueSeperation = Mathf.Sin(Mathf.PI / 2 - Mathf.Abs(Mathf.Atan(testx))) * testheight; //Atan(x) is the angle a line of slope x makes if (trueSeperation > currentSeparation) { candidatePoint = new Vector2(testx, (upperSegment.Y(testx) + lowerSegment.Y(testx)) / 2); currentSeparation = trueSeperation; return; } }; //initialize segments lowerSegment = new LineSegment(lowerhull[0], lowerhull[1]); upperSegment = new LineSegment(upperhull[0], upperhull[1]); //Break when one of the two lists is completly traversed while (upperhullIterator < upperhull.Count - 1 && lowerhullIterator < lowerhull.Count - 1) { //The part between currentx(exclusive) and nextx(inclusive) is under scrutiny //we advance the segment that ends on the smallest x if (upperhull[upperhullIterator].x < lowerhull[lowerhullIterator].x) { upperhullIterator++; upperSegment = new LineSegment(upperhull[upperhullIterator - 1], upperhull[upperhullIterator]); } else { lowerhullIterator++; lowerSegment = new LineSegment(lowerhull[lowerhullIterator - 1], lowerhull[lowerhullIterator]); } if (lowerSegment.IsVertical || upperSegment.IsVertical) { continue; //skip this iteration } nextx = Mathf.Min(upperSegment.XInterval.Max, lowerSegment.XInterval.Max); nextheight = upperSegment.Y(nextx) - lowerSegment.Y(nextx); if (nextheight < 0) { throw new GeomException(); } testCandidatePoint(nextx, nextheight); //also points inbetween vertices float heightchange = (nextheight - currentheight) / (nextx - currentx); float baseheigth = nextheight - nextx * heightchange; float candidatex = heightchange / baseheigth; if (new FloatInterval(currentx, nextx).Contains(candidatex)) { var candidateheigth = baseheigth + heightchange * candidatex; testCandidatePoint(candidatex, candidateheigth); } //save variables for next iteration currentheight = nextheight; currentx = nextx; } return(new LineSeparation(PointLineDual.Dual(candidatePoint), currentSeparation)); }