Skip to content

Commit

Permalink
Introduced MultiShapeTiling base class (#36)
Browse files Browse the repository at this point in the history
* Moved common code into base class
* Updated control layout
  • Loading branch information
schokee committed Mar 31, 2024
1 parent c570f04 commit 61ec350
Show file tree
Hide file tree
Showing 6 changed files with 335 additions and 438 deletions.
211 changes: 41 additions & 170 deletions Fovero.Model/Tiling/DijonTiling.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,124 +3,31 @@

namespace Fovero.Model.Tiling;

public sealed class DijonTiling : ITiling
public sealed class DijonTiling(ushort columns, ushort rows, float spacing = 0.7f) : MultiShapeTiling(columns, rows, spacing)
{
private float Spacing { get; }

public DijonTiling(ushort columns, ushort rows, float spacing = 0.7f)
protected override ITile? CreateTile(ushort ordinal, Location location, IReadOnlyDictionary<Location, ITile> lookup)
{
Spacing = spacing;
Columns = (ushort)(columns * 2 - 1);
Rows = (ushort)(rows * 2 - 1);
Bounds = new Rectangle(0, 0, OffsetAt(Columns), OffsetAt(Rows)).ToScaledUnits();
var evenCol = location.Column % 2 == 0;
var evenRow = location.Row % 2 == 0;

return evenCol
? evenRow
? null
: new VerticalTile(this, ordinal, location, lookup)
: evenRow
? new HorizontalTile(this, ordinal, location, lookup)
: new QuadrilateralTile(this, ordinal, location, lookup);
}

public ushort Columns { get; }

public ushort Rows { get; }

public Rectangle Bounds { get; }

public Func<int, int, bool> Mask { get; set; } = (c, r) => c % 2 == 1 && r % 2 == 1;

public IEnumerable<ITile> Generate()
{
var allLocations = Enumerable
.Range(0, Rows)
.SelectMany(r => Enumerable
.Range(0, Columns)
.Where(c => !Mask(c, r))
.Select(c => new Location(c, r)));

ushort ordinal = 0;
var lookup = new Dictionary<Location, ITile>();

foreach (var location in allLocations)
{
var oddCol = location.Column % 2 == 0;
var oddRow = location.Row % 2 == 0;

ITile? tile =
!oddCol && !oddRow ? new SquareTile(this, ordinal, location, lookup) :
oddCol && !oddRow ? new VerticalTile(this, ordinal, location, lookup) :
oddRow && !oddCol ? new HorizontalTile(this, ordinal, location, lookup) : null;

if (tile is null)
{
continue;
}

lookup.Add(location, tile);
++ordinal;
}

return lookup.Values;
}

private float OffsetAt(int n)
{
return (1 + Spacing) * (n >> 1) + n % 2;
}

private Point2D OffsetAt(Location location)
{
return new Point2D(OffsetAt(location.Column), OffsetAt(location.Row));
}

private readonly struct Location(int column, int row)
{
public int Column { get; init; } = column;

public int Row { get; init; } = row;
}

private abstract class Tile : ITile
private sealed class VerticalTile : Tile
{
protected Location Location { get; }
private readonly IReadOnlyDictionary<Location, ITile> _lookup;

protected Tile(DijonTiling format, ushort ordinal, Location location, IReadOnlyDictionary<Location, ITile> lookup)
{
_lookup = lookup;

Location = location;
Ordinal = ordinal;
Bounds = new Rectangle(format.OffsetAt(location), format.OffsetAt(new Location(location.Column + 1, location.Row + 1))).ToScaledUnits();
}

public ushort Ordinal { get; }

public Point2D Center => Bounds.Center;

public Rectangle Bounds { get; protected init; }

public abstract IEnumerable<IEdge> Edges { get; }

public override string ToString()
{
return Ordinal.ToString();
}

protected IEdge CreateBorder(Location neighbor, Point2D start, Point2D end)
{
return _lookup.TryGetValue(neighbor, out var tile)
? Edge.CreateShared(start, end, this, tile)
: Edge.CreateBorder(start, end, this);
}
}
private readonly float _midPoint;

private sealed class SquareTile(DijonTiling format, ushort ordinal, Location location, IReadOnlyDictionary<Location, ITile> lookup)
: Tile(format, ordinal, location, lookup)
{
private IEnumerable<Point2D> CornerPoints
public VerticalTile(MultiShapeTiling format, ushort ordinal, Location location, IReadOnlyDictionary<Location, ITile> lookup)
: base(format, ordinal, location, lookup)
{
get
{
yield return new Point2D(Bounds.Left, Bounds.Top);
yield return new Point2D(Bounds.Right, Bounds.Top);
yield return new Point2D(Bounds.Right, Bounds.Bottom);
yield return new Point2D(Bounds.Left, Bounds.Bottom);
}
_midPoint = Bounds.Width / 2;
Bounds = new Rectangle(new Point2D(Bounds.Left, Bounds.Top - _midPoint), new Point2D(Bounds.Right, Bounds.Bottom + _midPoint));
}

public override IEnumerable<IEdge> Edges
Expand All @@ -130,34 +37,24 @@ public override IEnumerable<IEdge> Edges
return CornerPoints
.Repeat()
.Pairwise((start, end) => (Start: start, End: end))
.Take(4)
.Take(6)
.Select((segment, edge) =>
{
var neighbor = edge switch
{
0 => Location with { Row = Location.Row - 1 },
1 => Location with { Column = Location.Column + 1 },
2 => Location with { Row = Location.Row + 1 },
3 => Location with { Column = Location.Column - 1 },
0 => new Location(Location.Column - 1, Location.Row - 1),
1 => new Location(Location.Column + 1, Location.Row - 1),
2 => Location with { Column = Location.Column + 1 },
3 => new Location(Location.Column + 1, Location.Row + 1),
4 => new Location(Location.Column - 1, Location.Row + 1),
5 => Location with { Column = Location.Column - 1 },
_ => Location
};
return CreateBorder(neighbor, segment.Start, segment.End);
return CreateEdge(neighbor, segment.Start, segment.End);
});
}
}
}

private sealed class VerticalTile : Tile
{
private readonly float _midPoint;

public VerticalTile(DijonTiling format, ushort ordinal, Location location, IReadOnlyDictionary<Location, ITile> lookup)
: base(format, ordinal, location, lookup)
{
_midPoint = Bounds.Width / 2;
Bounds = new Rectangle(new Point2D(Bounds.Left, Bounds.Top - _midPoint), new Point2D(Bounds.Right, Bounds.Bottom + _midPoint));
}

private IEnumerable<Point2D> CornerPoints
{
Expand All @@ -171,58 +68,19 @@ private IEnumerable<Point2D> CornerPoints
yield return new Point2D(Bounds.Left, Bounds.Bottom - _midPoint);
}
}

public override IEnumerable<IEdge> Edges
{
get
{
return CornerPoints
.Repeat()
.Pairwise((start, end) => (Start: start, End: end))
.Take(6)
.Select((segment, edge) =>
{
var neighbor = edge switch
{
0 => new Location(Location.Column - 1, Location.Row - 1),
1 => new Location(Location.Column + 1, Location.Row - 1),
2 => Location with { Column = Location.Column + 1 },
3 => new Location(Location.Column + 1, Location.Row + 1),
4 => new Location(Location.Column - 1, Location.Row + 1),
5 => Location with { Column = Location.Column - 1 },
_ => Location
};
return CreateBorder(neighbor, segment.Start, segment.End);
});
}
}
}

private sealed class HorizontalTile : Tile
{
private readonly float _midPoint;

public HorizontalTile(DijonTiling format, ushort ordinal, Location location, IReadOnlyDictionary<Location, ITile> lookup)
public HorizontalTile(MultiShapeTiling format, ushort ordinal, Location location, IReadOnlyDictionary<Location, ITile> lookup)
: base(format, ordinal, location, lookup)
{
_midPoint = Bounds.Height / 2;
Bounds = new Rectangle(new Point2D(Bounds.Left - _midPoint, Bounds.Top), new Point2D(Bounds.Right + _midPoint, Bounds.Bottom));
}

private IEnumerable<Point2D> CornerPoints
{
get
{
yield return new Point2D(Bounds.Left, Bounds.Top + _midPoint);
yield return new Point2D(Bounds.Left + _midPoint, Bounds.Top);
yield return new Point2D(Bounds.Right - _midPoint, Bounds.Top);
yield return new Point2D(Bounds.Right, Bounds.Top + _midPoint);
yield return new Point2D(Bounds.Right - _midPoint, Bounds.Bottom);
yield return new Point2D(Bounds.Left + _midPoint, Bounds.Bottom);
}
}

public override IEnumerable<IEdge> Edges
{
get
Expand All @@ -244,9 +102,22 @@ public override IEnumerable<IEdge> Edges
_ => Location
};
return CreateBorder(neighbor, segment.Start, segment.End);
return CreateEdge(neighbor, segment.Start, segment.End);
});
}
}

private IEnumerable<Point2D> CornerPoints
{
get
{
yield return new Point2D(Bounds.Left, Bounds.Top + _midPoint);
yield return new Point2D(Bounds.Left + _midPoint, Bounds.Top);
yield return new Point2D(Bounds.Right - _midPoint, Bounds.Top);
yield return new Point2D(Bounds.Right, Bounds.Top + _midPoint);
yield return new Point2D(Bounds.Right - _midPoint, Bounds.Bottom);
yield return new Point2D(Bounds.Left + _midPoint, Bounds.Bottom);
}
}
}
}
Loading

0 comments on commit 61ec350

Please sign in to comment.