/// <summary> /// Initializes a new instance of the <see cref="Polygon" /> class. /// </summary> /// <param name="segment">The segment.</param> public Polygon(ILineSegment segment) { this.LineSegments = ImmutableArray.Create(segment); this.innerPath = new InternalPath(segment, true); this.path = new PolygonPath(this); this.Paths = ImmutableArray.Create <IPath>(this.path); }
private void ReducePath(PolygonPath polyPath, ReductionSettings settings, ReductionSettings toughestSettings = null) { if (settings.ReduceByMinDistance && (toughestSettings == null || !toughestSettings.ReduceByMinDistance || settings.MinDistance > toughestSettings.MinDistance) ) { polyPath.ReduceByMinDistance(settings.MinDistance, settings.MinVertexCount); } if (settings.ReduceByMinTriangleArea && (toughestSettings == null || !toughestSettings.ReduceByMinTriangleArea || settings.MinTriangleArea > toughestSettings.MinTriangleArea) ) { float worldScale = Common.WorldScale(transform); float globalScaleSq = worldScale * worldScale; polyPath.ReduceByMinTriangleArea(settings.MinTriangleArea / globalScaleSq, settings.MinVertexCount); } if (settings.ReduceCodirected && (toughestSettings == null || !toughestSettings.ReduceCodirected || settings.MinAngle > toughestSettings.MinAngle) ) { polyPath.ReduceCodirected(settings.MinAngle * Mathf.Deg2Rad, settings.MinVertexCount); } }
private static void WritePath(StringBuilder builder, string name, PolygonPath path) { // Write path type builder.Append($"{name.ToUpper()}{Environment.NewLine}"); var first = true; // Write polygons foreach (var polygon in path) { var copy = new Polygon(polygon); if (first && name == "SOLUTION" && copy.Orientation != PolygonOrientation.CounterClockwise) { first = false; copy.Orientation = PolygonOrientation.CounterClockwise; } copy.OrderBottomLeftFirst(); // Write points for polygon foreach (var point in copy) { builder.Append($"{point.X},{point.Y} "); } // New line at end of polygon builder.Append(Environment.NewLine); } }
public void NewClipperFileBasedTest() { var testData = LoadTestHelper.LoadFromFile("TestData/tests.txt"); foreach (var test in testData.Values) { var clipper = new Clipper.Clipper(); clipper.AddPath(test.Subjects, PolygonKind.Subject); clipper.AddPath(test.Clips, PolygonKind.Clip); var solution = new PolygonTree(); Assert.IsTrue(clipper.Execute(test.ClipOperation, solution, false, test.FillType)); var path = new PolygonPath(solution.AllPolygons.Select(n => n.Polygon).ToList()); // TODO: reinclude these tests once test data is verified. var ignoreTestNumbers = new[] { 36, 38, 39, 44, 46, 48, 51, 52, 59, 64, 67, 69 }; if (ignoreTestNumbers.Contains(test.TestNumber)) { continue; } Assert.AreEqual(test.Solution.Count, path.Count, $"{test.TestNumber}: {test.Caption}"); // Match points, THIS IS DESTRUCTIVE TO BOTH THE TEST DATA AND RESULT DATA. Assert.IsTrue(AreSame(test, path)); // If we had an exact match then both solutions should now be empty. Assert.AreEqual(0, test.Solution.Count, $"{test.TestNumber}: {test.Caption}"); Assert.AreEqual(0, path.Count, $"{test.TestNumber}: {test.Caption}"); } }
public static List <List <ClipperLib.IntPoint> > ToOriginal(this PolygonPath path) { return(path .Select(subject => subject .Select(s => new ClipperLib.IntPoint(s.X, s.Y)) .ToList()) .ToList()); }
/// <summary> /// Test to see if the two paths are the same in that: /// 1. They have the same polygon count. /// 2. There are matching polygons in that: /// a. Same point count. /// b. Same orientation. /// </summary> public static bool AreSame(ClipExecutionData test, PolygonPath path2) { if (test.Solution.Count != path2.Count) { return(false); } for (var i = 0; i < path2.Count; i++) { var polygon1 = path2[i]; // Order to make comparison easier. polygon1.OrderBottomLeftFirst(); for (var j = 0; j < test.Solution.Count; j++) { var polygon2 = test.Solution[j]; // Vertex counts need to match if (polygon1.Count != polygon2.Count) { continue; } // Orientations need to match if (polygon1.Orientation != polygon2.Orientation) { continue; } // Count the number of points that match in order across the polygons. // Given both polygons are ordered by bottom left, then // points at each index should match if they are the same polygon. var pointMatchCount = polygon1 .Where((point, k) => point == polygon2[k]) .Count(); // Did the point match count equal the vertex count? if (pointMatchCount != polygon1.Count) { continue; } // This is a matching polygon so remove from both solutions // and decrement outer loop index. path2.RemoveAt(i--); test.Solution.RemoveAt(j); // break from inner loop to outer loop break; } } return(true); }
private static bool ReadPolygon(PolygonPath path, IReadOnlyList <string> lines, ref int lineNumber) { const string matchPolygonPointLine = @"^\s*[\+-]?\s*\d+\s*,\s*[\+-]?\s*\d+\s*"; const string matchPointValues = @"\s*([\+-]?\s*\d+\s*),\s*([\+-]?\s*\d+)\s*"; // EOF? if (lineNumber == lines.Count) { return(false); } var match = Regex.Match(lines[lineNumber], matchPolygonPointLine); if (!match.Success) { return(false); } var line = lines[lineNumber++].Trim(); var polygon = new Polygon(); while (line.Length > 0) { // Read point match = Regex.Match(line, matchPointValues); // It should be a match for valid values as we only get here if line has length > 0, and we remove whitespace from begining. if (!match.Success) { throw new Exception($"Invalid point value at line {lineNumber} ==> {line}"); } var x = int.Parse(match.Groups[1].Value.Replace(" ", "")); var y = int.Parse(match.Groups[2].Value.Replace(" ", "")); polygon.Add(new PointL(x, y)); // Skip past match line = line.Substring(match.Length); } // Order to make comparison easier. polygon.OrderBottomLeftFirst(); path.Add(polygon); return(true); }
private void PerformTrace() { var spriteRenderer = GetComponent <SpriteRenderer> (); var sprite = spriteRenderer.sprite; var ms = new MarchingSquares( sprite.texture, alphaThreshold: AlphaThreshold, clockWise: ClockWise, mipLevel: MipLevel ); polyPath = ms.TraceContours(); float pixelsPerUnit = RasterHelper.GetPixelsPerUnit(sprite); float scale = (1 << MipLevel) / pixelsPerUnit; var pivot = RasterHelper.GetPivot(sprite); var offset = -Vector2.Scale(sprite.bounds.size, pivot); polyPath.Scale(scale); polyPath.Translate(offset); int pointCountBeforeOpt = polyPath.TotalPointCount; float worldScale = Common.WorldScale(spriteRenderer.transform); if (ReduceByMinDistance) { polyPath.ReduceByMinDistance(MinDistance, MinVertexCount); } if (ReduceByMinTriangleArea) { float globalScaleSq = worldScale * worldScale; polyPath.ReduceByMinTriangleArea(MinTriangleArea / globalScaleSq, MinVertexCount); } if (ReduceCodirected) { polyPath.ReduceCodirected(MinAngle * Mathf.Deg2Rad, MinVertexCount); } int pointCountAfterOpt = polyPath.TotalPointCount; print(string.Format( "Reduction: {0}/{1} ({2:P})", pointCountAfterOpt, pointCountBeforeOpt, 1.0f - ( float )pointCountAfterOpt / pointCountBeforeOpt )); }
private static PolygonPath ReadPath(string pathName, IReadOnlyList <string> lines, ref int lineNumber) { // Get the path name var name = lines[lineNumber++].Trim(); if (!name.Equals(pathName, StringComparison.CurrentCultureIgnoreCase)) { throw new Exception($"Expecting path name '{pathName}' at line {lineNumber}."); } // Create a new path var path = new PolygonPath(); // Read polygons into path. while (ReadPolygon(path, lines, ref lineNumber)) { } return(path); }
public void ParsePolygonsFromFile() { this.ClearBodiesList(); if (PlistPath == null) { return; } Hashtable plistData = new Hashtable(); PListManager.ParsePListFile(PlistPath.text, ref plistData); Hashtable bodies = plistData["bodies"] as Hashtable; if (bodies == null) { Debug.Log("Bodies not found"); } ArrayList keyNames = new ArrayList(bodies.Keys); _totalPolygonsinFile = new PolygonObject[keyNames.Count]; if (keyNames != null) { for (int i = 0; i < keyNames.Count; i++) { _totalPolygonsinFile[i] = new PolygonObject(); _totalPolygonsinFile[i].bodyname = keyNames[i] as String; Hashtable bodyDic = bodies[keyNames[i]] as Hashtable; ArrayList fixtures = bodyDic["fixtures"] as ArrayList; var totalPaths = new List <PolygonPath>(); for (var f = 0; f < fixtures.Count; f++) { var fixture = fixtures[f] as Hashtable; if (fixture == null) { continue; } var polygonsArray = fixture["polygons"] as ArrayList; if (polygonsArray == null) { continue; } for (int j = 0; j < polygonsArray.Count; j++) { ArrayList pointArray = polygonsArray[j] as ArrayList; PolygonPath tempPath = new PolygonPath(); Vector2[] pointsVector = new Vector2[pointArray.Count]; for (int k = 0; k < pointsVector.Length; k++) { string pointInString = pointArray[k] as String; pointsVector[k] = this.ConvertToVector2FromString(pointInString); } tempPath.points = pointsVector; totalPaths.Add(tempPath); } } _totalPolygonsinFile[i].paths = totalPaths.ToArray(); } Array.Sort(_totalPolygonsinFile); this.setPolygonOfIndex(selectedIndex); } else { Debug.Log("Keys not found"); } }
private void SetTest() { if (!_testData.ContainsKey(_testNumber)) { return; } // Get test of interest. var test = _testData[_testNumber]; _testSubject = test.Subjects.FirstOrDefault(); _testClip = test.Clips.FirstOrDefault(); _testSolution = test.Solution; _newClipperSolution = new PolygonPath(); var clipper = new Clipper.Clipper(); clipper.AddPath(test.Subjects, PolygonKind.Subject); clipper.AddPath(test.Clips, PolygonKind.Clip); clipper.Execute(ClipOperation.Union, _newClipperSolution); _testBoundary = _testSolution.Any() ? BoundaryBuilder .BuildPolygonBoundary(_testSolution.First(), PolygonKind.Subject) .ToList() : null; _newClipperBoundary = _newClipperSolution.Any() ? BoundaryBuilder .BuildPolygonBoundary(_newClipperSolution.First(), PolygonKind.Subject) .ToList() : null; var originalClipperSolution = new List <List <IntPoint> >(); var originalClipper = new ClipperLib.Clipper(); originalClipper.AddPaths(test.Subjects.ToOriginal(), PolyType.ptSubject, true); originalClipper.AddPaths(test.Clips.ToOriginal(), PolyType.ptClip, true); originalClipper.Execute(ClipType.ctUnion, originalClipperSolution); _originalClipperSolution = originalClipperSolution.ToNew(); _originalClipperBoundary = _originalClipperSolution.Any() ? BoundaryBuilder .BuildPolygonBoundary(_originalClipperSolution.First(), PolygonKind.Subject) .ToList() : null; Program.VisualizerForm.ClipperViewControl.Subjects = new[] { new PolygonViewModel { IsOpen = false, EdgeColor = Color.LawnGreen, VertexColor = Color.DarkGreen, Items = _testSubject?.ToVertices() } }; Program.VisualizerForm.ClipperViewControl.Clips = new[] { new PolygonViewModel { IsOpen = false, EdgeColor = Color.Blue, VertexColor = Color.DarkBlue, Items = _testClip?.ToVertices() } }; _solutionType = SolutionType.Test; SetSolution(); solutionComboBox.SelectedIndex = 0; }
public PolygonPath TraceContours() { float traceStart = Time.realtimeSinceStartup; var polyPath = new PolygonPath(); var polysWithHoles = polyPath.PolysWithHoles; contoursByPoints = new Dictionary <int, Polygon> (); int i = 0; for (int y = 0; y < height; y++) { bool prevWasSolid = false; PolygonWithHoles container = null; for (int x = 0; x < width; x++, i++) { bool curIsSolid = IsSolidByIndex(i); if (prevWasSolid != curIsSolid) { var contour = GetContourByPoint(x, y); if (contour == null) { if (curIsSolid) { // This is the beginning of new outer contour. contour = TraceContour(x, y); container = new PolygonWithHoles(contour); contour.Container = container; polysWithHoles.Add(container); } else { // This is a new hole. It must be traced and linked with current container. contour = TraceContour(x, y); contour.Container = container; container.Holes.Add(contour); container = null; } } else { if (curIsSolid) { /* We've entered a contour. * When contour is an outer -> store contour container in container var. * When contour is a hole -> do the same thing. */ container = contour.Container; } else { /* We've exited a contour. * When contour is an outer -> set container to null. * When contour is a hole -> do the same thing. */ container = null; } } } prevWasSolid = curIsSolid; } } float traceTime = Time.realtimeSinceStartup - traceStart; // DEBUG if (false) { Debug.Log( "MarchingSquares.TraceContours ()" + " time: " + traceTime + ", width: " + width + ", height: " + height + ", numPixels: " + numPixels + ", pixels per sec: " + (numPixels / traceTime).ToString("F") ); } return(polyPath); }
public void ParsePolygonsFromFile() { this.ClearBodiesList(); if (PlistPath == null) { return; } Hashtable plistData = new Hashtable(); PListManager.ParsePListFile(PlistPath.text, ref plistData); Hashtable bodies = plistData["bodies"] as Hashtable; if (bodies == null) { Debug.Log("Bodies not found"); } ArrayList keyNames = new ArrayList(bodies.Keys); _totalPolygonsinFile = new PolygonObject[keyNames.Count]; if (keyNames != null) { for (int i = 0; i < keyNames.Count; i++) { _totalPolygonsinFile[i] = new PolygonObject(); _totalPolygonsinFile[i].bodyname = keyNames[i] as String; Hashtable bodyDic = bodies[keyNames[i]] as Hashtable; /*Using single fixture because unity support single fixture*/ ArrayList fixtures = bodyDic["fixtures"] as ArrayList; Hashtable fixture1 = fixtures[0] as Hashtable; ArrayList polygonsArray = fixture1["polygons"] as ArrayList; PolygonPath[] totalPaths = new PolygonPath[polygonsArray.Count]; for (int j = 0; j < totalPaths.Length; j++) { ArrayList pointArray = polygonsArray[j] as ArrayList; PolygonPath tempPath = new PolygonPath(); Vector2[] pointsVector = new Vector2[pointArray.Count]; for (int k = 0; k < pointsVector.Length; k++) { string pointInString = pointArray[k] as String; pointsVector[k] = this.ConvertToVector2FromString(pointInString); } tempPath.points = pointsVector; totalPaths[j] = tempPath; } _totalPolygonsinFile[i].paths = totalPaths; } Array.Sort(_totalPolygonsinFile); this.setPolygonOfIndex(selectedIndex); } else { Debug.Log("Keys not found"); } }