Gondwana Game Engine Development (master) - 2.6.1
Gondwana is a cross-platform 2.5D game and rendering engine written in C#/.NET 8. It provides fine-grained control over rendering, timing, and scene composition, with built-in support for parallax, z-ordering, pixel overhang, collision detection, and particle effects. Gondwana targets desktop and web platforms using SkiaSharp for graphics and pluggable audio backends for desktop and browser targets.
Loading...
Searching...
No Matches
HexAxialPointedTop.cs
1using System.Drawing;
3
5
10internal sealed class HexAxialPointedTop : ISceneLayerCoordinates
11{
18 public Point GetAnchorPixelAtSceneLayerCoordinates(SceneLayer sceneLayer, PointF gp)
19 {
20 int width = sceneLayer.TileWidth;
21 int height = sceneLayer.TileHeight;
22
23 // The layout uses even-r offset coordinates. Interpolate the
24 // staggered X offset between the surrounding integer rows.
25 int baseRow = (int)MathF.Floor(gp.Y);
26 float rowProgress = gp.Y - baseRow;
27
28 // Preserve the existing integer-division behavior for odd tile widths.
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);
33 var origin = sceneLayer.OriginPx;
34
35 float x = -origin.X + (gp.X * width) + offsetX;
36 float y = -origin.Y + (gp.Y * height * 0.75f);
37
38 return new Point(
39 (int)MathF.Floor(x),
40 (int)MathF.Floor(y));
41 }
42
50 public PointF GetSceneLayerCoordinatesAtPixel(SceneLayer sceneLayer, PointF pixelPt)
51 {
52 int W = sceneLayer.TileWidth;
53 int H = sceneLayer.TileHeight;
54
55 int originX = sceneLayer.OriginPx.X;
56 int originY = sceneLayer.OriginPx.Y;
57
58 float fy = (pixelPt.Y + originY) / (H * 0.75f);
59 int approxRow = (int)Math.Round(fy);
60
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);
64
65 var best = new Point(approxCol, approxRow);
66 float bestDist = float.MaxValue;
67
68 foreach (var cand in NeighborsPointedTop(approxCol, approxRow, includeSelf: true))
69 {
70 var poly = HexPolygonPointedTop(sceneLayer, cand.X, cand.Y, includeOverhang: false);
71
72 // Only here: round once for PIP test
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);
75
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; }
80 }
81 return new PointF(best.X, best.Y);
82 }
83
91 public List<SceneLayerTile> GetSceneLayerTilesInPixelRange(SceneLayer sceneLayer, Rectangle worldPixelRange, bool includeOverhang)
92 {
93 var result = new List<SceneLayerTile>();
94 int W = sceneLayer.TileWidth; int H = sceneLayer.TileHeight;
95 var candidateRange = TileBounds.GetTransformedTileCandidateRange(sceneLayer, worldPixelRange, includeOverhang);
96
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;
99
100 for (int row = minRow; row <= maxRow; row++)
101 {
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;
105
106 for (int col = minCol; col <= maxCol; col++)
107 {
108 var gp = sceneLayer[col, row]; if (gp == null) continue;
109 var r = GetPixelRangeForTile(gp, includeOverhang);
110 if (r.IntersectsWith(worldPixelRange)) result.Add(gp);
111 }
112 }
113 return result;
114 }
115
122 public Rectangle GetPixelRangeForTile(Tile tile, bool includeOverhang)
123 {
124 var p = GetAnchorPixelAtSceneLayerCoordinates(tile.SceneLayer, tile.SceneLayerCoordinates);
125 int W = tile.SceneLayer.TileWidth; int H = tile.SceneLayer.TileHeight;
126 var rect = new Rectangle(p.X, p.Y, W, H);
127 return TileBounds.ApplyTileGeometry(rect, tile, includeOverhang);
128 }
129
136 public Rectangle GetPixelRangeForTileList(List<Tile> tileList, bool includeOverhang)
137 {
138 Rectangle ret = Rectangle.Empty;
139 foreach (var t in tileList)
140 {
141 var r = GetPixelRangeForTile(t, includeOverhang);
142 ret = ret.IsEmpty ? r : Rectangle.Union(ret, r);
143 }
144 return ret;
145 }
146
154 public SceneLayerTile GetAdjacentSceneLayerTile(SceneLayerTile gp, CardinalDirections dir)
155 {
156 int x = gp.GridCoordinatesAbs.X; int y = gp.GridCoordinatesAbs.Y;
157 bool even = (y & 1) == 0; var m = gp.SceneLayer;
158 return dir switch
159 {
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,
168 _ => null
169 };
170 }
171
178 public Point[] GetPolygonPts(Tile tile, bool includeOverhang)
179 {
180 return HexPolygonPointedTop(tile.SceneLayer, (int)tile.SceneLayerCoordinates.X, (int)tile.SceneLayerCoordinates.Y, includeOverhang);
181 }
182
190 public PointF FindEquivalentSceneLayerCoordinates(PointF valColRow, int xUpperBound, int yUpperBound)
191 {
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);
197 }
198
199 // Helpers ---------------------------------------------------
200 private static IEnumerable<Point> NeighborsPointedTop(int col, int row, bool includeSelf)
201 {
202 if (includeSelf) yield return new Point(col, row);
203 bool even = (row & 1) == 0;
204 yield return new Point(col, row - 1); // N
205 yield return new Point(col, row + 1); // S
206 yield return new Point(col + (even ? 0 : 1), row - 1); // NE
207 yield return new Point(col - (even ? 1 : 0), row - 1); // NW
208 yield return new Point(col + (even ? 0 : 1), row + 1); // SE
209 yield return new Point(col - (even ? 1 : 0), row + 1); // SW
210 }
211
212 private static Point[] HexPolygonPointedTop(SceneLayer sceneLayer, int col, int row, bool includeOverhang)
213 {
214 int W = sceneLayer.TileWidth;
215 int H = sceneLayer.TileHeight;
216
217 int originX = sceneLayer.OriginPx.X;
218 int originY = sceneLayer.OriginPx.Y;
219
220 var p = new Point(
221 -originX + col * W + ((row & 1) == 0 ? 0 : W / 2),
222 -originY + (int)Math.Floor(row * (H * 0.75f)));
223
224 var rect = new Rectangle(p.X, p.Y, W, H);
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;
227
228 // Pointed-top vertices from bounding box
229 return new[]
230 {
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)
237 };
238 }
239
240 private static bool PointInPolygon(Point[] poly, Point p)
241 {
242 bool inside = false; int j = poly.Length - 1;
243 for (int i = 0; i < poly.Length; i++)
244 {
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;
248 }
249 return inside;
250 }
251}
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.
Definition Spacing.cs:12