Skip to content

Instantly share code, notes, and snippets.

@BrianMacIntosh
Created November 3, 2013 21:37
Show Gist options
  • Save BrianMacIntosh/7295134 to your computer and use it in GitHub Desktop.
Save BrianMacIntosh/7295134 to your computer and use it in GitHub Desktop.
This class provides a mechanism to iterate through the pixels in a rasterized line. The pixels are returned in order by distance from the point 'start'. The iterator acts like a raycast, not like e.g. Bresenham's line algorithm, in that it returns every pixel intersected by the ray.
using System;
// RasterLineIterator.cs
// 3 November 2013
// Author: Brian MacIntosh
/// <summary>
/// A simple two-dimensional vector. Replace with any 2d vector class.
/// </summary>
class Vector2
{
public float X;
public float Y;
public Vector2()
: this(0, 0)
{
}
public Vector2(float X, float Y)
{
this.X = X;
this.Y = Y;
}
}
/// <summary>
/// This class provides a mechanism to iterate through the pixels in a rasterized
/// line. The pixels are returned in order by distance from the point 'start'.
/// The iterator acts like a raycast, not like e.g. Bresenham's line algorithm,
/// in that it returns every pixel intersected by the ray.
/// </summary>
class RasterLineIterator
{
double nextX;
double nextY;
double incX;
double incY;
/// <summary>
/// Set to false when the iterator reaches the last pixel.
/// </summary>
bool notended;
/// <summary>
/// Set to false when the iterator passes the last pixel.
/// </summary>
bool valid;
Vector2 start;
Vector2 cur;
Vector2 dir = new Vector2();
Vector2 end;
/// <summary>
/// Cached value. Negative indicates it must be recalculated.
/// </summary>
float progress;
/// <summary>
/// Construct a new iterator starting at the point 'start' and ending at the point 'end'.
/// </summary>
public RasterLineIterator(Vector2 start, Vector2 end)
{
set(start, end);
}
/// <summary>
/// Reset this iterator using the specified start and end points.
/// </summary>
public void set(Vector2 start, Vector2 end)
{
//Clamp start and end to the pixel grid
start.X = (int)start.X;
start.Y = (int)start.Y;
end.X = (int)end.X;
end.Y = (int)end.Y;
notended = start.X != end.X || start.Y != end.Y;
valid = true;
progress = -1;
this.cur = this.start = start;
this.end = end;
dir.X = Math.Sign(end.X - start.X);
dir.Y = Math.Sign(end.Y - start.Y);
if (end.X == start.X)
incX = 0;
else
incX = 1 / (double)Math.Abs(end.X - start.X);
if (end.Y == start.Y)
incY = 0;
else
incY = 1 / (double)Math.Abs(end.Y - start.Y);
nextX = incX;
nextY = incY;
}
/// <summary>
/// Advance the iterator to the next pixel.
/// </summary>
public void next()
{
if (!notended)
valid = false;
if (dir.Y == 0)
{
cur.X += dir.X;
}
else if (dir.X == 0)
{
cur.Y += dir.Y;
}
else
{
//Go to next axis cross
if (nextX < nextY)
{
nextX += incX;
cur.X += dir.X;
}
else
{
nextY += incY;
cur.Y += dir.Y;
}
}
//Invalidate progress calculation
progress = -1;
if (cur.X == end.X && cur.Y == end.Y)
{
notended = false;
}
}
/// <summary>
/// Returns true if the iterator can be advanced and remain within the valid range of the line.
/// </summary>
public bool hasNext()
{
return notended;
}
/// <summary>
/// Returns false if the iterator has passed the end of the line.
/// </summary>
public bool isValid()
{
return valid;
}
/// <summary>
/// Returns the current location of the iterator.
/// </summary>
public Vector2 current()
{
return cur;
}
/// <summary>
/// Returns a value from 0 to 1 indicating the progress of the iterator from start to end.
/// This value is cached for you.
/// </summary>
public float getProgress()
{
if (progress < 0)
{
if (start.X == end.X)
{
if (start.Y == end.Y)
progress = 1;
else
progress = (cur.Y - start.Y) / (end.Y - start.Y);
}
else
progress = (cur.X - start.X) / (end.X - start.X);
}
return progress;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment