Прямая — это ряд смежных точек. Мы ограничимся рассмотрением двух типов прямых:
При построении этих линий точка за точкой меняется только одна координата, а другая остается неизменной. Точка — это объект, состоящий из координат x и y. Мы определяем и используем ее следующим образом:
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
4 We can access fields of an object with a dot. In this case, we access the x and y coordinates of the point p with p.x and p.y.
1 Определение точки как объекта
2 Координаты x и y — целые числа
3 Переменная p является экземпляром класса Point с координатами (5, 7).
4 Мы можем обращаться к полям объекта через точку. В данном случае мы обращаемся к координатам x и y точки p через p.x и p.y.
Линия может быть задана начальной и конечной точками или начальной точкой, длиной и направлением. Мы рассмотрим оба подхода.
Мы можем задать линию по её начальной точке, длине и направлению. Имея эту информацию, мы можем рисовать линию попиксельно.
Длина линии — это положительное целое число (нуль — это просто точка, а не линия; отрицательное значение длины физически невозможно). Направление может быть горизонтальным или вертикальным.
Это значит, что мы будем ограничиваться построением линий слева направо (указываем самую левую точку линии, а затем увеличиваем координату x) или сверху вниз (увеличиваем координату y).
Для начала можно создать два похожих процесса: один для рисования горизонтальных линий, другой — для рисования вертикальных.
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
Positive, который является подтипом int и содержит только положительные числа.p..q — это (встроенный) итератор, который выполняет итерацию от p до q (оба конца включительно) в порядке возрастания, то есть для выполнения итерации p должно быть меньше q. В данном случае мы выполняем итерацию от нуля до length.y остается постоянной, и на каждом шаге итерации мы рисуем пиксель на один пиксель правее, чем на предыдущем шаге.x остается неизменной, а координата y увеличивается, чтобы нарисовать новый пиксель.У нас может быть одна процедура drawLine, которая будет рисовать как горизонтальные, так и вертикальные линии в зависимости от заданного пользователем параметра.
Для моделирования набора из ограниченного количества значений мы используем тип enum. Преимущества этого выбора станут очевидны позже, когда мы рассмотрим одну из особенностей языка Nim — проверку полноты перечисления.
Теперь мы можем дополнить предыдущий пример следующими строками:
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)
enum) с двумя возможными значениями.Direction.switch. Он похож на оператор if, но более функционален. Мы не только получаем отдельные ветви для каждого варианта, но и проверяем полноту: мы не можем случайно пропустить какой-либо вариант. В частности, мы должны учесть все значения перечисления.Если мы используем две точки для определения прямой, нам не нужно знать (или вычислять) длину прямой, а направление можно определить по расположению этих двух точек: если у них одинаковые координаты по оси Y, мы рисуем горизонтальную прямую, а если одинаковые координаты по оси X — вертикальную.
Мы всегда будем начинать с точки с меньшим значением изменяемой координаты, но позволим пользователям указывать точки в любом порядке.
Этого можно добиться с помощью рекурсии.
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
x — изменяющаяся переменная, а y остается постоянной.r.y меньше, чем p.y, это, в свою очередь, приведёт к вызову функции drawVerticalLine(r, p) и нарисует вертикальную линию между точками (20, -10) и (20, 20)