10internal sealed class HexAxialFlatTopCoordinates : ISceneLayerCoordinates
18 public Point GetAnchorPixelAtSceneLayerCoordinates(SceneLayer sceneLayer, PointF gp)
25 int baseColumn = (int)MathF.Floor(gp.X);
26 float columnProgress = gp.X - baseColumn;
29 float halfHeight = height / 2;
30 float currentColumnOffsetY = (baseColumn & 1) == 0 ? 0f : halfHeight;
31 float nextColumnOffsetY = ((baseColumn + 1) & 1) == 0 ? 0f : halfHeight;
32 float offsetY = currentColumnOffsetY + ((nextColumnOffsetY - currentColumnOffsetY) * columnProgress);
35 float x = -origin.X + (gp.X * width * 0.75f);
36 float y = -origin.Y + (gp.Y * height) + offsetY;
38 return new Point((
int)MathF.Floor(x), (
int)MathF.Floor(y));
48 public PointF GetSceneLayerCoordinatesAtPixel(SceneLayer sceneLayer, PointF pixelPt)
53 float originX = sceneLayer.
OriginPx.X;
54 float originY = sceneLayer.
OriginPx.Y;
59 float dxFromCol0Center = pixelPt.X - (-originX +
W * 0.5f);
62 float colF = dxFromCol0Center / (
W * 0.75f);
63 int approxCol = (int)Math.Round(colF);
72 + ((approxCol & 1) == 0 ? 0f : H * 0.5f);
74 float dyFromRow0Center = pixelPt.Y - baseCenterY;
75 float rowF = dyFromRow0Center / H;
76 int approxRow = (int)Math.Round(rowF);
78 var best =
new Point(approxCol, approxRow);
79 float bestDist =
float.MaxValue;
83 foreach (var cand
in NeighborsFlatTop(approxCol, approxRow, includeSelf:
true))
85 var poly = HexPolygonFlatTop(sceneLayer, cand.X, cand.Y, includeOverhang:
false);
87 var pInt =
new Point((
int)Math.Round(pixelPt.X), (
int)Math.Round(pixelPt.Y));
88 if (PointInPolygon(poly, pInt))
89 return new PointF(cand.X, cand.Y);
92 float cx = -originX + cand.X * (
W * 0.75f) +
W / 2f;
93 float cy = -originY + cand.Y * (float)H
94 + ((cand.X & 1) == 0 ? 0 : H / 2) + H / 2f;
96 float dx = cx - pixelPt.X;
97 float dy = cy - pixelPt.Y;
98 float d = dx * dx + dy * dy;
107 return new PointF(best.X, best.Y);
117 public List<SceneLayerTile> GetSceneLayerTilesInPixelRange(SceneLayer sceneLayer, Rectangle worldPixelRange,
bool includeOverhang)
119 var result =
new List<SceneLayerTile>();
123 int originX = sceneLayer.
OriginPx.X;
124 int originY = sceneLayer.
OriginPx.Y;
125 var candidateRange = TileBounds.GetTransformedTileCandidateRange(sceneLayer, worldPixelRange, includeOverhang);
127 int minCol = (int)Math.Floor((candidateRange.Left + originX) / (
W * 0.75f)) - 2;
128 int maxCol = (int)Math.Ceiling((candidateRange.Right + originX) / (
W * 0.75f)) + 2;
130 for (
int col = minCol; col <= maxCol; col++)
132 int yOffset = ((col & 1) == 0 ? 0 : H / 2);
133 int minRow = (int)Math.Floor((candidateRange.Top + originY - yOffset) / (
float)H) - 2;
134 int maxRow = (int)Math.Ceiling((candidateRange.Bottom + originY - yOffset) / (
float)H) + 2;
136 for (
int row = minRow; row <= maxRow; row++)
138 var gp = sceneLayer[col, row];
139 if (gp ==
null)
continue;
140 var r = GetPixelRangeForTile(gp, includeOverhang);
141 if (r.IntersectsWith(worldPixelRange))
155 public Rectangle GetPixelRangeForTile(Tile tile,
bool includeOverhang)
157 var p = GetAnchorPixelAtSceneLayerCoordinates(tile.SceneLayer, tile.SceneLayerCoordinates);
158 int W = tile.SceneLayer.TileWidth;
int H = tile.SceneLayer.TileHeight;
160 return TileBounds.ApplyTileGeometry(rect, tile, includeOverhang);
169 public Rectangle GetPixelRangeForTileList(List<Tile> tileList,
bool includeOverhang)
172 foreach (var t
in tileList)
174 var r = GetPixelRangeForTile(t, includeOverhang);
175 ret = ret.IsEmpty ? r :
Rectangle.Union(ret, r);
187 public SceneLayerTile GetAdjacentSceneLayerTile(SceneLayerTile gp,
CardinalDirections dir)
190 bool even = (x & 1) == 0;
194 CardinalDirections.E => m.ResolveWrappedTile(x + 1, y),
195 CardinalDirections.W => m.ResolveWrappedTile(x - 1, y),
196 CardinalDirections.NE => m.ResolveWrappedTile(x + 1, y - (even ? 1 : 0)),
197 CardinalDirections.SE => m.ResolveWrappedTile(x + 1, y + (even ? 0 : 1)),
198 CardinalDirections.NW => m.ResolveWrappedTile(x - 1, y - (even ? 1 : 0)),
199 CardinalDirections.SW => m.ResolveWrappedTile(x - 1, y + (even ? 0 : 1)),
200 CardinalDirections.N =>
null,
201 CardinalDirections.S =>
null,
212 public Point[] GetPolygonPts(Tile tile,
bool includeOverhang)
214 return HexPolygonFlatTop(tile.SceneLayer, (
int)tile.SceneLayerCoordinates.X, (
int)tile.SceneLayerCoordinates.Y, includeOverhang);
224 public PointF FindEquivalentSceneLayerCoordinates(PointF valColRow,
int xUpperBound,
int yUpperBound)
227 float modX = valColRow.X % (xUpperBound + 1);
228 float modY = valColRow.Y % (yUpperBound + 1);
229 if (modX < 0) modX += xUpperBound + 1;
230 if (modY < 0) modY += yUpperBound + 1;
231 return new PointF(modX, modY);
236 private static IEnumerable<Point> NeighborsFlatTop(
int col,
int row,
bool includeSelf)
238 if (includeSelf) yield
return new Point(col, row);
239 bool even = (col & 1) == 0;
240 yield
return new Point(col + 1, row);
241 yield
return new Point(col - 1, row);
242 yield
return new Point(col + 1, row + (even ? 0 : 1));
243 yield
return new Point(col + 1, row - (even ? 1 : 0));
244 yield
return new Point(col - 1, row + (even ? 0 : 1));
245 yield
return new Point(col - 1, row - (even ? 1 : 0));
248 private static Point[] HexPolygonFlatTop(SceneLayer sceneLayer,
int col,
int row,
bool includeOverhang)
253 int originX = sceneLayer.
OriginPx.X;
254 int originY = sceneLayer.
OriginPx.Y;
257 -originX + (
int)Math.Floor(col * (
W * 0.75f)),
258 -originY + row * H + ((col & 1) == 0 ? 0 : H / 2));
261 var ohRect = TileBounds.ApplyOverhang(
272 new Point(x +
W/4, y),
273 new Point(x + 3*
W/4, y),
274 new Point(x +
W, y + H/2),
275 new Point(x + 3*
W/4, y + H),
276 new Point(x +
W/4, y + H),
277 new Point(x, y + H/2)
281 private static bool PointInPolygon(Point[] poly, Point p)
283 bool inside =
false;
int j = poly.Length - 1;
284 for (
int i = 0; i < poly.Length; i++)
286 bool intersect = ((poly[i].Y > p.Y) != (poly[j].Y > p.Y)) &&
287 (p.X < (poly[j].X - poly[i].X) * (p.Y - poly[i].Y) / (
float)(poly[j].Y - poly[i].Y) + poly[i].X);
288 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.