Skip to content

Commit

Permalink
Change VirtualSurfaceControl.cs to pan based on world coordinates
Browse files Browse the repository at this point in the history
  • Loading branch information
Vrabbers committed Jun 19, 2024
1 parent c074b2c commit e796a03
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 68 deletions.
1 change: 1 addition & 0 deletions .idea/.idea.BnbnavNetClient/.idea/avalonia.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

106 changes: 68 additions & 38 deletions BnbnavNetClient/Controls/VirtualSurfaceControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,55 @@ private struct Tile { public SKSurface Surface; public bool Dirty; }
private const int TileSideExponent = 9;
private const int TileSide = 1 << TileSideExponent; // 256

private readonly Dictionary<TileIndex, Tile> tileMap = [];
private uint renderSequenceNumber;
private readonly Dictionary<TileIndex, Tile> _tileMap = [];

private uint _renderSequenceNumber;

private Rect _renderBounds;
private double _scale = 0.5;

public double Scale
{
get => _scale;
set
{
_scale = value;
InvalidateVisual();
}
}

private Point _pan = new(0, 0);
public Point Pan
{
get => _pan;
set
{
_pan = value;
InvalidateVisual();
}
}

public void PanAndScale(Point pan, double scale)
{
_pan = pan;
_scale = scale;
InvalidateVisual();
}

public abstract void DrawTile(TileSurface surface, Rect worldCoordinates);

public void InvalidateTiles(PixelRect pixelBounds)
public void InvalidateTiles(Rect worldBounds)
{
Interlocked.Increment(ref renderSequenceNumber);
Interlocked.Increment(ref _renderSequenceNumber);

var (topLeftTile, bottomRightTile) = GetExtents(pixelBounds);
var (topLeftTile, bottomRightTile) = GetWorldExtends(worldBounds);

for (int x = topLeftTile.X; x <= bottomRightTile.X; x++)
for (var x = topLeftTile.X; x <= bottomRightTile.X; x++)
{
for (int y = topLeftTile.Y; y <= bottomRightTile.Y; y++)
for (var y = topLeftTile.Y; y <= bottomRightTile.Y; y++)
{
var tileIndex = new TileIndex(x, y);
ref var tile = ref CollectionsMarshal.GetValueRefOrNullRef(tileMap, tileIndex);
ref var tile = ref CollectionsMarshal.GetValueRefOrNullRef(_tileMap, tileIndex);
if (!Unsafe.IsNullRef(ref tile))
{
tile.Dirty = true;
Expand All @@ -50,11 +82,11 @@ public void InvalidateTiles(PixelRect pixelBounds)

public void InvalidateTiles()
{
Interlocked.Increment(ref renderSequenceNumber);
Interlocked.Increment(ref _renderSequenceNumber);

foreach (var key in tileMap.Keys)
foreach (var key in _tileMap.Keys)
{
ref var tile = ref CollectionsMarshal.GetValueRefOrNullRef(tileMap, key);
ref var tile = ref CollectionsMarshal.GetValueRefOrNullRef(_tileMap, key);
tile.Dirty = true;
}

Expand All @@ -78,11 +110,11 @@ void ICustomDrawOperation.Render(ImmediateDrawingContext context)
return;
}

var originalSequenceNumber = renderSequenceNumber;
var originalSequenceNumber = _renderSequenceNumber;

var (panX, panY) = Pan * Scale * dpiScale;

var viewportRect = renderBounds * dpiScale;
var viewportRect = _renderBounds * dpiScale;
var pixelBounds = new PixelRect((int)panX, (int)panY, (int)viewportRect.Width, (int)viewportRect.Height);

var (topLeftTile, bottomRightTile) = GetExtents(pixelBounds);
Expand All @@ -91,40 +123,43 @@ void ICustomDrawOperation.Render(ImmediateDrawingContext context)

canvas.SetMatrix(canvas.TotalMatrix.PostConcat(SKMatrix.CreateScale((float)dpiScale, (float)dpiScale).Invert()));

for (int x = topLeftTile.X; x <= bottomRightTile.X; x++)
for (var x = topLeftTile.X; x <= bottomRightTile.X; x++)
{
for (int y = topLeftTile.Y; y <= bottomRightTile.Y; y++)
for (var y = topLeftTile.Y; y <= bottomRightTile.Y; y++)
{
var tileIndex = new TileIndex(x, y);
ref var tile = ref CollectionsMarshal.GetValueRefOrAddDefault(tileMap, tileIndex, out bool exists);
ref var tile = ref CollectionsMarshal.GetValueRefOrAddDefault(_tileMap, tileIndex, out var exists);

if (!exists || tile.Dirty)
{
var surface = tile.Surface ??= SKSurface.Create(lease.GrContext, false, new SKImageInfo(TileSide, TileSide));
var surfaceCanvas = surface.Canvas;
var worldCoordinates = new Rect(x * TileSide, y * TileSide, TileSide, TileSide) * (1 / (Scale * dpiScale));

surfaceCanvas.Save();

surfaceCanvas.Scale((float)(Scale * dpiScale));
surfaceCanvas.Translate((-worldCoordinates.TopLeft).ToSKPoint());

DrawTile(new TileSurface
{
Surface = surface,
Canvas = surfaceCanvas,
CanvasSize = new(TileSide, TileSide)
},
new Rect(x * TileSide, y * TileSide, TileSide, TileSide) * Scale);
CanvasSize = new SKSizeI(TileSide, TileSide)
}, worldCoordinates);

surfaceCanvas.Restore();

// If a region of the virtual surface was invalidated, the tile we're rendering may be outdated
// Another thread is racing to mark those tiles as dirty, so avoid overwriting that
// A redraw is queued, so any glitches will be fixed on the next redraw
if (originalSequenceNumber == renderSequenceNumber)
if (originalSequenceNumber == _renderSequenceNumber)
{
tile.Dirty = false;
}
}

canvas.DrawSurface(tile.Surface, new((tileIndex.X << TileSideExponent) - (float)panX, (tileIndex.Y << TileSideExponent) - (float)panY));
canvas.DrawSurface(tile.Surface, new SKPoint((tileIndex.X << TileSideExponent) - (float)panX, (tileIndex.Y << TileSideExponent) - (float)panY));
}
}

Expand All @@ -137,9 +172,7 @@ private Point ToScreen(Point viewportPoint)

return mtx.Transform(viewportPoint);
}

public double Scale { get; set; } = 1;


private static (TileIndex TopLeft, TileIndex BottomRight) GetExtents(PixelRect pixelBounds)
{
var topLeft = new TileIndex(pixelBounds.X >> TileSideExponent, pixelBounds.Y >> TileSideExponent);
Expand All @@ -148,26 +181,23 @@ private static (TileIndex TopLeft, TileIndex BottomRight) GetExtents(PixelRect p
return (topLeft, bottomRight);
}

private Point pan = new(0, 512);
public Point Pan
private static (TileIndex TopLeft, TileIndex BottomRight) GetWorldExtends(Rect worldBounds)
{
get => pan;
set
{
pan = value;
InvalidateVisual();
}
}
var top = (int)double.Floor(worldBounds.Bottom / TileSide);
var left = (int)double.Floor(worldBounds.Left / TileSide);
var bottom = (int)double.Ceiling(worldBounds.Bottom / TileSide);
var right = (int)double.Ceiling(worldBounds.Right / TileSide);

return (new TileIndex(left, top), new TileIndex(right, bottom));
}

public override void Render(DrawingContext context)
{
renderBounds = new(0, 0, Bounds.Width, Bounds.Height);
_renderBounds = new Rect(0, 0, Bounds.Width, Bounds.Height);
context.Custom(this);
}

private Rect renderBounds;

Rect ICustomDrawOperation.Bounds => renderBounds;

Rect ICustomDrawOperation.Bounds => _renderBounds;

bool ICustomDrawOperation.HitTest(Point p) => true;
bool IEquatable<ICustomDrawOperation>.Equals(ICustomDrawOperation? other) => this == other;
Expand Down
3 changes: 3 additions & 0 deletions BnbnavNetClient/Models/IntRect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ public bool Contains(int x, int y) =>

public IntRect Expand(int amt) =>
new(Left - amt, Top - amt, Right + amt, Bottom + amt);

public IntRect Intersect(IntRect x) =>
new(int.Max(Left, x.Left), int.Max(Top, x.Top), int.Min(Right, x.Right), int.Min(Bottom, x.Bottom));
}
3 changes: 2 additions & 1 deletion BnbnavNetClient/Models/MapBin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public void Insert(Edge edge)

public void Query(IntRect rect, List<Node> nodes, List<Edge> edges)
{
rect = rect.Intersect(Bounds);
var startX = (rect.Left - Bounds.Left - BinSideLength / 2) / BinSideLength;
var startY = (rect.Top - Bounds.Top - BinSideLength / 2) / BinSideLength;
var endX = (rect.Right - Bounds.Left + BinSideLength / 2) / BinSideLength;
Expand All @@ -89,7 +90,7 @@ public void Query(IntRect rect, List<Node> nodes, List<Edge> edges)
{
for (var j = startY; j <= endY; j++)
{
ref var bin = ref _bins[i, j];
var bin = _bins[i, j];
if (bin is null)
continue;
foreach (var node in bin.Nodes)
Expand Down
41 changes: 12 additions & 29 deletions BnbnavNetClient/Views/VirtualMapView.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ protected override void OnPointerMoved(PointerEventArgs e)

if (currentPosition.Properties.IsLeftButtonPressed)
{
Pan += (previousPointerPosition - currentPosition.Position);
Pan += (previousPointerPosition - currentPosition.Position) / Scale;
MapViewModel.Pan = Pan;
}

Expand All @@ -61,7 +61,7 @@ protected override void OnPointerWheelChanged(PointerWheelEventArgs e)

var currentPosition = e.GetPosition(this);

var deltaScale = e.Delta.Y * scale / 10.0;
var deltaScale = e.Delta.Y * Scale / 10.0;
Zoom(deltaScale, e.GetPosition(this));
}

Expand All @@ -76,27 +76,24 @@ public void HandlePinchForZoom(object? sender, PinchEventArgs e)

public void Zoom(double deltaScale, Point origin)
{
var clampedScale = double.Clamp(scale + deltaScale, 0.1, 20.0);
var clampedScale = double.Clamp(Scale + deltaScale, 0.1, 20.0);

var worldPrevPos = ToWorld(origin);

scale = clampedScale;
Scale = clampedScale;

var worldFutureIncorrectPos = ToWorld(origin);

WrongPoint = worldFutureIncorrectPos;
var correction = worldFutureIncorrectPos - worldPrevPos;
Pan -= correction * scale;
Pan -= correction;
MapViewModel.Pan = Pan;

InvalidateTiles();
}

private Point ToWorld(Point viewportPoint)
{
var mtx = Matrix.CreateScale(scale, scale) * Matrix.CreateTranslation(-Pan);

return mtx.Invert().Transform(viewportPoint);
return (viewportPoint / Scale) + Pan;
}

private MapViewResources resources = new()
Expand All @@ -123,13 +120,7 @@ private Point ToWorld(Point viewportPoint)

public override void DrawTile(TileSurface surface, Rect worldCoordinates)
{
SKPoint ToTile(Point point)
{
return point.ToSKPoint() - new SKPoint(tile.TopLeft.X, tile.TopLeft.Y);
}

Debug.WriteLine($"Rendering tile {tile.TopLeft} with scale " + scale);

Debug.WriteLine($"Rendering tile {worldCoordinates.TopLeft} with scale " + Scale);

var canvas = surface.Canvas;

Expand Down Expand Up @@ -164,31 +155,23 @@ SKPoint ToTile(Point point)
// {
// return MapViewModel.HighlightInterWorldNodesEnabled && mapService.AllEdges.Where(edge => edge.From.Id == node.Id || edge.To.Id == node.Id).Any(edge => edge.From.World != edge.To.World);
// }).ToList();

// canvas.Scale((float)scale, (float)scale, -tile.X, -tile.Y);


var map = MapViewModel.MapService;

var nodes = new List<Node>();
var edges = new List<Edge>();

var scaled = SKMatrix.CreateScale((float)scale, (float)scale).MapRect(new SKRect(tile.X, tile.Y, tile.Right, tile.Bottom));


//var queryRegion = new IntRect((int)scaled.Left, (int)scaled.Top, (int)scaled.Right, (int)scaled.Bottom);

//map.MapBins.Query(queryRegion, nodes, edges);

edges = map.Edges.Values.ToList();
var queryRegion = new IntRect((int)worldCoordinates.Left, (int)worldCoordinates.Top, (int)worldCoordinates.Right, (int)worldCoordinates.Bottom);

map.MapBins.Query(queryRegion, nodes, edges);

foreach (var edge in edges)
{
//if (noRender.Contains(edge))
// continue;

var from = ToTile(edge.From.Point);
var to = ToTile(edge.To.Point);
var from = edge.From.Point.ToSKPoint();
var to = edge.To.Point.ToSKPoint();

DrawEdge(canvas, edge.Road.RoadType, from, to, drawRoute: false);
}
Expand Down

0 comments on commit e796a03

Please sign in to comment.