10internal sealed class HexAxialPointedTop : ISceneLayerCoordinates
18 public Point GetAnchorPixelAtSceneLayerCoordinates(SceneLayer sceneLayer, PointF gp)
25 int baseRow = (int)MathF.Floor(gp.Y);
26 float rowProgress = gp.Y - baseRow;
29 float halfWidth = width / 2;
30 float currentRowOffsetX = (baseRow & 1) == 0 ? 0f : halfWidth;
31 float nextRowOffsetX = ((baseRow + 1) & 1) == 0 ? 0f : halfWidth;
32 float offsetX = currentRowOffsetX + ((nextRowOffsetX - currentRowOffsetX) * rowProgress);
35 float x = -origin.X + (gp.X * width) + offsetX;
36 float y = -origin.Y + (gp.Y * height * 0.75f);
50 public PointF GetSceneLayerCoordinatesAtPixel(SceneLayer sceneLayer, PointF pixelPt)
58 float fy = (pixelPt.Y + originY) / (H * 0.75f);
59 int approxRow = (int)Math.Round(fy);
61 int baseX = -originX + ((approxRow & 1) == 0 ? 0 :
W / 2);
62 float fx = (pixelPt.X - baseX) / (
float)
W;
63 int approxCol = (int)Math.Round(fx);
65 var best =
new Point(approxCol, approxRow);
66 float bestDist =
float.MaxValue;
68 foreach (var cand
in NeighborsPointedTop(approxCol, approxRow, includeSelf:
true))
70 var poly = HexPolygonPointedTop(sceneLayer, cand.X, cand.Y, includeOverhang:
false);
73 var pInt =
new Point((
int)Math.Round(pixelPt.X), (
int)Math.Round(pixelPt.Y));
74 if (PointInPolygon(poly, pInt))
return new PointF(cand.X, cand.Y);
76 float cx = -sceneLayer.OriginPx.X + cand.X * (float)
W + ((cand.Y & 1) == 0 ? 0 :
W / 2f) +
W / 2f;
77 float cy = -sceneLayer.OriginPx.Y + cand.Y * (H * 0.75f) + H / 2f;
78 float d = (cx - pixelPt.X) * (cx - pixelPt.X) + (cy - pixelPt.Y) * (cy - pixelPt.Y);
79 if (d < bestDist) { bestDist = d; best = cand; }
81 return new PointF(best.X, best.Y);
91 public List<SceneLayerTile> GetSceneLayerTilesInPixelRange(SceneLayer sceneLayer, Rectangle worldPixelRange,
bool includeOverhang)
93 var result =
new List<SceneLayerTile>();
95 var candidateRange = TileBounds.GetTransformedTileCandidateRange(sceneLayer, worldPixelRange, includeOverhang);
97 int minRow = (int)Math.Floor((candidateRange.Top + sceneLayer.
OriginPx.Y) / (H * 0.75f)) - 2;
98 int maxRow = (int)Math.Ceiling((candidateRange.Bottom + sceneLayer.
OriginPx.Y) / (H * 0.75f)) + 2;
100 for (
int row = minRow; row <= maxRow; row++)
102 int xOffset = ((row & 1) == 0 ? 0 :
W / 2);
103 int minCol = (int)Math.Floor((candidateRange.Left + sceneLayer.
OriginPx.X - xOffset) / (
float)
W) - 2;
104 int maxCol = (int)Math.Ceiling((candidateRange.Right + sceneLayer.
OriginPx.X - xOffset) / (
float)
W) + 2;
106 for (
int col = minCol; col <= maxCol; col++)
108 var gp = sceneLayer[col, row];
if (gp ==
null)
continue;
109 var r = GetPixelRangeForTile(gp, includeOverhang);
110 if (r.IntersectsWith(worldPixelRange)) result.Add(gp);
122 public Rectangle GetPixelRangeForTile(Tile tile,
bool includeOverhang)
124 var p = GetAnchorPixelAtSceneLayerCoordinates(tile.SceneLayer, tile.SceneLayerCoordinates);
125 int W = tile.SceneLayer.TileWidth;
int H = tile.SceneLayer.TileHeight;
127 return TileBounds.ApplyTileGeometry(rect, tile, includeOverhang);
136 public Rectangle GetPixelRangeForTileList(List<Tile> tileList,
bool includeOverhang)
139 foreach (var t
in tileList)
141 var r = GetPixelRangeForTile(t, includeOverhang);
142 ret = ret.IsEmpty ? r :
Rectangle.Union(ret, r);
154 public SceneLayerTile GetAdjacentSceneLayerTile(SceneLayerTile gp,
CardinalDirections dir)
157 bool even = (y & 1) == 0; var m = gp.
SceneLayer;
160 CardinalDirections.N => m.ResolveWrappedTile(x, y - 1),
161 CardinalDirections.S => m.ResolveWrappedTile(x, y + 1),
162 CardinalDirections.NE => m.ResolveWrappedTile(x + (even ? 0 : 1), y - 1),
163 CardinalDirections.NW => m.ResolveWrappedTile(x - (even ? 1 : 0), y - 1),
164 CardinalDirections.SE => m.ResolveWrappedTile(x + (even ? 0 : 1), y + 1),
165 CardinalDirections.SW => m.ResolveWrappedTile(x - (even ? 1 : 0), y + 1),
166 CardinalDirections.E =>
null,
167 CardinalDirections.W =>
null,
178 public Point[] GetPolygonPts(Tile tile,
bool includeOverhang)
180 return HexPolygonPointedTop(tile.SceneLayer, (
int)tile.SceneLayerCoordinates.X, (
int)tile.SceneLayerCoordinates.Y, includeOverhang);
190 public PointF FindEquivalentSceneLayerCoordinates(PointF valColRow,
int xUpperBound,
int yUpperBound)
192 float modX = valColRow.X % (xUpperBound + 1);
193 float modY = valColRow.Y % (yUpperBound + 1);
194 if (modX < 0) modX += xUpperBound + 1;
195 if (modY < 0) modY += yUpperBound + 1;
196 return new PointF(modX, modY);
200 private static IEnumerable<Point> NeighborsPointedTop(
int col,
int row,
bool includeSelf)
202 if (includeSelf) yield
return new Point(col, row);
203 bool even = (row & 1) == 0;
204 yield
return new Point(col, row - 1);
205 yield
return new Point(col, row + 1);
206 yield
return new Point(col + (even ? 0 : 1), row - 1);
207 yield
return new Point(col - (even ? 1 : 0), row - 1);
208 yield
return new Point(col + (even ? 0 : 1), row + 1);
209 yield
return new Point(col - (even ? 1 : 0), row + 1);
212 private static Point[] HexPolygonPointedTop(SceneLayer sceneLayer,
int col,
int row,
bool includeOverhang)
217 int originX = sceneLayer.
OriginPx.X;
218 int originY = sceneLayer.
OriginPx.Y;
221 -originX + col *
W + ((row & 1) == 0 ? 0 :
W / 2),
222 -originY + (
int)Math.Floor(row * (H * 0.75f)));
225 var ohRect = TileBounds.ApplyOverhang(rect, includeOverhang ?
new Spacing(0, 0, 0, 0) :
Spacing.None, includeOverhang);
226 int x = ohRect.X;
int y = ohRect.Y;
231 new Point(x +
W/2, y),
232 new Point(x +
W, y + H/4),
233 new Point(x +
W, y + 3*H/4),
234 new Point(x +
W/2, y + H),
235 new Point(x, y + 3*H/4),
236 new Point(x, y + H/4)
240 private static bool PointInPolygon(Point[] poly, Point p)
242 bool inside =
false;
int j = poly.Length - 1;
243 for (
int i = 0; i < poly.Length; i++)
245 bool intersect = ((poly[i].Y > p.Y) != (poly[j].Y > p.Y)) &&
246 (p.X < (poly[j].X - poly[i].X) * (p.Y - poly[i].Y) / (
float)(poly[j].Y - poly[i].Y) + poly[i].X);
247 if (intersect) inside = !inside; j = i;
Point GridCoordinatesAbs
Gets the absolute grid coordinates of this tile within its parent scene layer as integer values.
override SceneLayer SceneLayer
Gets the parent scene layer that contains this tile.
Point OriginPx
Gets or sets the world-space pixel origin of this layer's (0,0) tile position.
int TileHeight
Gets or sets the height of each tile in this layer, measured in pixels.
int TileWidth
Gets or sets the width of each tile in this layer, measured in pixels.
CardinalDirections
Represents the eight cardinal and intercardinal directions of the compass.
@ Rectangle
Particles are spawned uniformly within a rectangular area. This is the default distribution.
record struct Spacing([property:JsonProperty("left")] int Left, [property:JsonProperty("top")] int Top, [property:JsonProperty("right")] int Right, [property:JsonProperty("bottom")] int Bottom)
Represents dimensions in pixels when spacing Tilesheets.