A line is a series of adjacent points. We will limit ourselves to two types of lines: Horizontal and vertical lines
When drawing these lines point by point, just one coordinate changes,
while the other stays constant.
A point is an object consisting of x and y coordinates, and we define it and use it like this:
import pixels
type
Point = object # 1
x: int # 2
y: int
var p = Point(x: 5, y: 7) # 3
putPixel(p.x, p.y) # 4
A line can be defined by a starting and an ending point, or by a starting point and its length and a direction. We’ll show both approaches.
We can define a line by its starting point, its length, and its direction. Having these two information, we can then draw a line pixel by pixel.
A line length is a positive integer (a zero would be just a point, not a line; a negative value for the length is physically impossible). A direction can be either horizontal or vertical.
This means that we will limit ourselves to always create lines from left to right (we specify the leftmost point of a line and then increase the x coordinate) or top to bottom (we increase the y coordinate).
We can start by creating two similar procs: one for drawing horizontal lines, and one for drawing vertical lines.
import pixels
type
Point = object
x: int
y: int
proc drawHorizontalLine(start: Point; length: Positive) = # 1
for delta in 0 .. length: # 2
putPixel(start.x + delta, start.y) # 3
proc drawVerticalLine(start: Point; length: Positive) =
for delta in 0 .. length:
putPixel(start.x, start.y + delta) # 4
let a = Point(x: 60, y: 40)
drawHorizontalLine(a, 50) # 5
drawVerticalLine(a, 30) # 6
We can have one proc, drawLine, which will draw both horizontal and vertical lines, depending on the provided user’s parameter.
To model a set of a limited amount of values, we use the enum type. The benefits of this choice will be visible later, when we will see one of the features of Nim: exhaustiveness checking.
We can now extend the previous example with these lines:
type
Direction = enum 1
Horizontal 2
Vertical
proc drawLine(start: Point; length: Positive; direction: Direction) = 3
case direction 4
of Horizontal:
drawHorizontalLine(start, length) 5
of Vertical:
drawVerticalLine(start, length)
drawLine(a, 50, Horizontal) 6
drawLine(a, 30, Vertical)
If we use two points to define a line, we don’t have to know (or calculate) the line length, and the direction can be inferred from the relation of these two points: if they have the same y coordinate, we are drawing a horizontal line, and a vertical line between two points with the same x coordinate.
We will always iterate from a point with a smaller value of the changing coordinate, but we will allow our users to specify the points in any order.
We can achieve this with recursion.
proc drawHorizontalLine(a, b: Point) = # 1
if b.x < a.x:
drawHorizontalLine(b, a) # 2
else:
for x in a.x .. b.x:
putPixel(x, a.y) # 3
proc drawVerticalLine(a, b: Point) =
if b.y < a.y:
drawVerticalLine(b, a)
else:
for y in a.y .. b.y:
putPixel(a.x, y)
let
p = Point(x: 20, y: 20)
q = Point(x: 50, y: 20)
r = Point(x: 20, y: -10)
drawHorizontalLine(p, q) # 4
drawVerticalLine(p, r) # 5