public static void HexEdges(PathUtil.PathType type, out float[] edgeX, out float[] edgeY) { edgeX = (type == PathUtil.PathType.Hex) ? RenderUtil.HexEdgesX : RenderUtil.SquareEdgesX; edgeY = (type == PathUtil.PathType.Hex) ? RenderUtil.HexEdgesY : RenderUtil.SquareEdgesY; }
public BorderPath(Border border, Sector sector, PathUtil.PathType type) { float[] edgeX, edgeY; RenderUtil.HexEdges(type, out edgeX, out edgeY); int lengthEstimate = border.Path.Count() * 3; List<PointF> borderPathPoints = new List<PointF>(lengthEstimate); List<byte> borderPathTypes = new List<byte>(lengthEstimate); List<PointF> clipPathPoints = new List<PointF>(lengthEstimate); List<byte> clipPathTypes = new List<byte>(lengthEstimate); List<List<PointF>> curves = new List<List<PointF>>(lengthEstimate); List<PointF> curve = new List<PointF>(lengthEstimate); // Based on http://dotclue.org/t20/sec2pdf - J Greely rocks my world. int checkFirst = 0; int checkLast = 5; Hex startHex = Hex.Empty; bool startHexVisited = false; foreach (Hex hex in border.Path) { checkLast = checkFirst + 5; if (startHexVisited && hex == startHex) { // I'm in the starting hex, and I've been // there before, so stop testing at neighbor // 5, no matter what checkLast = 5; // degenerate case, entering for third time if (checkFirst < 3) break; } else if (!startHexVisited) { startHex = hex; startHexVisited = true; // PERF: This seems costly... analyze it! PointF newPoint = Astrometrics.HexToCenter(Astrometrics.LocationToCoordinates(new Location(sector.Location, hex))); newPoint.X += edgeX[0]; newPoint.Y += edgeY[0]; // MOVETO borderPathPoints.Add(newPoint); borderPathTypes.Add((byte)PathPointType.Start); // MOVETO clipPathPoints.Add(newPoint); clipPathTypes.Add((byte)PathPointType.Start); if (curve.Count > 1) { curves.Add(curve); curve = new List<PointF>(lengthEstimate); curve.Add(newPoint); } else { curve.Clear(); curve.Add(newPoint); } } PointF pt = Astrometrics.HexToCenter(Astrometrics.LocationToCoordinates(new Location(sector.Location, hex))); int i = checkFirst; for (int check = checkFirst; check <= checkLast; check++) { i = check; Hex neighbor = Astrometrics.HexNeighbor(hex, i % 6); if (border.Path.Contains(neighbor)) // TODO: Consider a hash here break; PointF newPoint = new PointF(pt.X + edgeX[(i + 1) % 6], pt.Y + edgeY[(i + 1) % 6]); // TODO: Replace this by clipping borders to sector bounds; problem - bottom/right edges protrude // Only actually render edges within the sector //if( 1 <= ( hex / 100 ) && ( hex / 100 ) <= 32 && 1 <= ( hex % 100 ) && ( hex % 100 ) <= 40 ) { // LINETO borderPathPoints.Add(newPoint); borderPathTypes.Add((byte)PathPointType.Line); //curve.Add( newPoint ); if (hex.IsValid) { curve.Add(newPoint); } else { if (curve.Count > 1) { curves.Add(curve); curve = new List<PointF>(lengthEstimate); curve.Add(newPoint); } else { curve.Clear(); curve.Add(newPoint); } } } /* else { // MOVETO if( borderPathTypes[ borderPathTypes.Count - 1 ] == (byte)PathPointType.Start ) { // Collapse multiple MOVETOs - makes GDI+ happy borderPathPoints[ borderPathPoints.Count - 1 ] = newPoint; } else { borderPathPoints.Add( newPoint ); borderPathTypes.Add( (byte)PathPointType.Start ); } if( curve.Count > 1 ) { curves.Add( curve ); curve = new List<PointF>( lengthEstimate ); curve.Add( newPoint ); } else { curve.Clear(); curve.Add( newPoint ); } } * */ // LINETO clipPathPoints.Add(newPoint); clipPathTypes.Add((byte)PathPointType.Line); } i = i % 6; // i is the direction to the next border hex, // and when we get there, we'll have come from // i + 3, so we start checking with i + 4. checkFirst = (i + 4) % 6; } // Trim trailing MOVETOs - makes GDI+ happy while (borderPathTypes.Count > 0 && borderPathTypes[borderPathTypes.Count - 1] == (byte)PathPointType.Start) { borderPathTypes.RemoveAt(borderPathTypes.Count - 1); borderPathPoints.RemoveAt(borderPathPoints.Count - 1); } borderPathTypes[borderPathTypes.Count - 1] |= (byte)PathPointType.CloseSubpath; this.borderPathPoints = borderPathPoints.ToArray(); this.borderPathTypes = borderPathTypes.ToArray(); this.clipPathPoints = clipPathPoints.ToArray(); this.clipPathTypes = clipPathTypes.ToArray(); if (curve.Count > 1) curves.Add(curve); curvePoints = curves.Select(c => c.ToArray()).ToArray(); }
internal ClipPath ComputeClipPath(PathUtil.PathType type) { lock (this) { if (clipPathsCache[(int)type] == null) clipPathsCache[(int)type] = new ClipPath(this, type); } return clipPathsCache[(int)type]; }
internal BorderPath ComputeGraphicsPath(Sector sector, PathUtil.PathType type) { lock (this) { if (borderPathsCache[(int)type] == null) borderPathsCache[(int)type] = new BorderPath(this, sector, type); } return borderPathsCache[(int)type]; }
public ClipPath(Sector sector, PathUtil.PathType borderPathType) { float[] edgex, edgey; RenderUtil.HexEdges(borderPathType, out edgex, out edgey); IEnumerable<Hex> hexes = Util.Sequence(1, Astrometrics.SectorWidth).Select(x => new Hex((byte)x, 1)) .Concat(Util.Sequence(2, Astrometrics.SectorHeight).Select(y => new Hex(Astrometrics.SectorWidth, (byte)y))) .Concat(Util.Sequence(Astrometrics.SectorWidth - 1, 1).Select(x => new Hex((byte)x, Astrometrics.SectorHeight))) .Concat(Util.Sequence(Astrometrics.SectorHeight - 1, 1).Select(y => new Hex(1, (byte)y))); Rectangle bounds = sector.Bounds; IEnumerable<Point> points = (from hex in hexes select new Point(hex.X + bounds.X, hex.Y + bounds.Y)).ToList(); PathUtil.ComputeBorderPath(points, edgex, edgey, out clipPathPoints, out clipPathPointTypes); PointF min = clipPathPoints[0]; PointF max = clipPathPoints[0]; for (int i = 1; i < clipPathPoints.Length; ++i) { PointF pt = clipPathPoints[i]; if (pt.X < min.X) min.X = pt.X; if (pt.Y < min.Y) min.Y = pt.Y; if (pt.X > max.X) max.X = pt.X; if (pt.Y > max.Y) max.Y = pt.Y; } this.bounds = new RectangleF(min, new SizeF(max.X - min.X, max.Y - min.Y)); }
public BorderPath(Border border, Sector sector, PathUtil.PathType type) { float[] edgeX, edgeY; RenderUtil.HexEdges(type, out edgeX, out edgeY); int lengthEstimate = border.Path.Count() * 3; List<PointF> points = new List<PointF>(lengthEstimate); List<byte> types = new List<byte>(lengthEstimate); LinkedList<LinkedList<PointF>> segments = new LinkedList<LinkedList<PointF>>(); LinkedList<PointF> currentSegment = new LinkedList<PointF>(); // Based on http://dotclue.org/t20/sec2pdf - J Greely rocks my world. int checkFirst = 0; int checkLast = 5; Hex startHex = Hex.Empty; bool startHexVisited = false; foreach (Hex hex in border.Path) { checkLast = checkFirst + 5; if (startHexVisited && hex == startHex) { // I'm in the starting hex, and I've been // there before, so stop testing at neighbor // 5, no matter what checkLast = 5; // degenerate case, entering for third time if (checkFirst < 3) break; } else if (!startHexVisited) { startHex = hex; startHexVisited = true; // PERF: This seems costly... analyze it! PointF newPoint = Astrometrics.HexToCenter(Astrometrics.LocationToCoordinates(new Location(sector.Location, hex))); newPoint.X += edgeX[0]; newPoint.Y += edgeY[0]; // MOVETO points.Add(newPoint); types.Add((byte)PathPointType.Start); // MOVETO currentSegment.AddLast(newPoint); } PointF pt = Astrometrics.HexToCenter(Astrometrics.LocationToCoordinates(new Location(sector.Location, hex))); int i = checkFirst; for (int check = checkFirst; check <= checkLast; check++) { i = check; Hex neighbor = Astrometrics.HexNeighbor(hex, i % 6); if (border.Path.Contains(neighbor)) // TODO: Consider a hash here break; PointF newPoint = new PointF(pt.X + edgeX[(i + 1) % 6], pt.Y + edgeY[(i + 1) % 6]); // LINETO points.Add(newPoint); types.Add((byte)PathPointType.Line); if (hex.IsValid) { // MOVETO currentSegment.AddLast(newPoint); } else { // LINETO if (currentSegment.Count > 1) segments.AddLast(currentSegment); currentSegment = new LinkedList<PointF>(); currentSegment.AddLast(newPoint); } } i = i % 6; // i is the direction to the next border hex, // and when we get there, we'll have come from // i + 3, so we start checking with i + 4. checkFirst = (i + 4) % 6; } types[types.Count - 1] |= (byte)PathPointType.CloseSubpath; if (currentSegment.Count > 1) segments.AddLast(currentSegment); this.points = points.ToArray(); this.types = types.ToArray(); // If last curve segment connects to first curve segment, merge them. // Example: Imperial border in Verge. if (segments.Count >= 2 && segments.First().First() == segments.Last().Last()) { var first = segments.First(); var last = segments.Last(); segments.RemoveFirst(); first.RemoveFirst(); foreach (var point in first) last.AddLast(point); } this.curves = segments.Select(c => { if (c.First() == c.Last()) { c.RemoveLast(); return new CurveSegment(c, true); } else { return new CurveSegment(c, false); } }).ToList(); }