Ремонт принтеров, сканнеров, факсов и остальной офисной техники


назад Оглавление вперед




[44]

экране отрезок между двумя указанными точками. Тогда мы можем создавать из списков отрезков рисовалки для изображений, состоящих из этих отрезков, вроде рисовалки wave с рисунка 2.10, таким образом:27

(define (segments->painter segment-list) (lambda (frame) (for-each (lambda (segment) (draw-line

((frame-coord-map frame) (start-segment segment)) ((frame-coord-map frame) (end-segment segment)))) segment-list)))

Отрезки даются в координатах по отношению к единичному квадрату. Для каждого сегмента в списке рисовалка преобразует концы отрезка с помощью отображения координат рамки и рисует отрезок между точками с преобразованными координатами.

Представление рисовалок в виде процедур воздвигает в языке построения изображений мощный барьер абстракции. Мы можем создавать и смешивать множество типов элементарных рисовалок, в зависимости от имеющихся возможностей графики. Детали их реализации несущественны. Любая процедура, если она принимает в качестве аргумента рамку и рисует в ней что-нибудь должным образом отмасштабированное, может служить рисовалкой.28

Упражнение 2.48.

Направленный отрезок на плоскости можно представить в виде пары векторов: вектор от начала координат до начала отрезка и вектор от начала координат до конца отрезка. Используйте свое представление векторов из упражнения 2.46 и определите представление отрезков с конструктором make-segment и селекторами start-segment и end-segment.

Упражнение 2.49.

С помощью segments->painter определите следующие элементарные рисовалки:

a.Рисовалку, которая обводит указанную рамку.

b.Рисовалку, которая рисует «Х», соединяя противоположные концы рамки.

c.Рисовалку, которая рисует ромб, соединяя между собой середины сторон рамки.

d.Рисовалку wave.

27Процедура segments->painter использует представление отрезков прямых, описанное ниже в упражнении 2.48. Кроме того, она использует процедуру for-each, описанную в упражнении 2.23.

28 Например, рисовалка rogers с рисунка 2.11 была получена из полутонового черно-белого изображения. Для каждой точки в указанной рамке рисовалка rogers определяет точку исходного изображения, которая в нее отображается, и соответствующим образом ее окрашивает. Разрешая себе иметь различные типы рисовалок, мы пользуемся идеей абстрактных данных, описанной в разделе 2.1.3, где мы говорили, что представление рациональных чисел может быть каким угодно, пока соблюдается соответствующее условие. Здесь мы используем то, что рисовалку можно реализовать как угодно, лишь бы она что-то изображала в указанной рамке. В разделе 2.1.3 показывается и то, как реализовать пары в виде процедур. Рисовалки - это наш второй пример процедурного представления данных.


Преобразование и комбинирование рисовалок

Операции над рисовалками (flip-vert или beside, например) создают новые рисовалки, которые вызывает исходные рисовалки по отношению к рамкам, производным от рамок-аргументов. Таким образом, скажем, flip-vert не требуется знать, как работает рисовалка, чтобы перевернуть ее - ей нужно только уметь перевернуть рамку вверх ногами: перевернутая рисовалка просто использует исходную, но в обращенной рамке.

Операции над рисовалками основываются на процедуре transform-painter, которая в качестве аргументов берет рисовалку и информацию о том, как преобразовать рамку, а возвращает новую рисовалку. Когда преобразованная рисовалка вызывается по отношению к какой-либо рамке, она преобразует рамку и вызывает исходную рисовалку по отношению к ней. Аргументами transform-painter служат точки (представленные в виде векторов), указывающие углы новой рамки: будучи отображенной на рамку, первая точка указывает исходную точку новой рамки, а две других - концы краевых векторов. Таким образом, аргументы, лежащие в пределах единичного квадрата, определяют рамку, которая содержится внутри исходной рамки.

(define (transform-painter painter origin corner1 corner2) (lambda (frame)

(let ((m (frame-coord-map frame))) (let ((new-origin (m origin))) (painter (make-frame new-origin

(sub-vect (m corner1) new-origin) (sub-vect (m corner2) new-origin)))))))

Вот как перевернуть изображение в рамке вертикально:

(define (flip-vert painter) (transform-painter painter

(make-vect 0.0 1.0) ; новая исходная точка (make-vect 1.0 1.0) ; новый конец edge1 (make-vect 0.0 0.0))) ; новый конец edge2

При помощи transform-painter нам нетрудно будет определять новые трансформации. Например, можно определить рисовалку, которая рисует уменьшенную копию исходного изображения в верхней правой четверти рамки:

(define (shrink-to-upper-right painter) (transform-painter painter

(make-vect 0.5 0.5) (make-vect 1.0 0.5)

(make-vect 0.5 1.0)))

Вот трансформация, которая поворачивает изображение на 90 градусов против часовой стрелки:29

(define (rotate90 painter) (transform-painter painter

29Rotate90 представляет собой чистый поворот только для квадратных рамок, поскольку она еще растягивает и сплющивает изображение так, чтобы оно уместилось в повернутой рамке.


(make-vect 1.0 0.0)

(make-vect 1.0 1.0) (make-vect 0.0 0.0)))

А эта сжимает изображение по направлению к центру рамки:30

(define (squash-inwards painter) (transform-painter painter

(make-vect 0.0 0.0)

(make-vect 0.65 0.35) (make-vect 0.35 0.65)))

Преобразования рамок являются также основой для определения средств комбинирования двух или более рисовалок. Например, процедура beside берет две рисовалки, трансформирует их так, чтобы они работали соответственно в левой и правой половинах рамки-аргумента, и создает новую составную ри-совалку. Когда составной рисовалке передается рамка, она вызывает первую из преобразованных рисовалок над левой половиной рамки, а вторую над правой половиной:

(define (beside painter1 painter2)

(let ((split-point (make-vect 0.5 0.0))) (let ((paint-left

(transform-painter painter1

(make-vect 0.0 split-point (make-vect 0.0

(paint-right (transform-painter painter2

split-point (make-vect 1.0 (make-vect 0.5

(lambda (frame)

(paint-left frame) (paint-right frame)))))

Обратите внимание, как абстракция данных, и особенно представление рисо-валок в виде процедур, облегчает реализацию beside. Процедуре beside не требуется ничего знать о деталях рисовалок-компонент, кроме того, что каждая из них что-то изобразит в указанной ей рамке.

Упражнение 2.50.

Определите преобразование flip-horiz, которое обращает изображение вокруг горизонтальной оси, а также преобразования, которые вращают рисовалки против часовой стрелки на 180 и 270 градусов.

Упражнение 2.51.

Определите для рисовалок операцию below. Below принимает в качестве аргументов две рисовалки. Когда получившейся рисовалке передается рамка, она рисует в нижней ее половине при помощи первой рисовалки, а в верхней при помощи второй. Определите below двумя способами - один раз аналогично процедуре beside, как она приведена выше, а второй раз через beside и операции вращения (см. упражнение 2.50).

30Ромбовидные изображения на рисунках 2.10 и 2.11 были получены с помощью squash-inwards, примененной к wave и rogers.

0.0)

1.0)))

0.0)

1.0))))



[стр.Начало] [стр.1] [стр.2] [стр.3] [стр.4] [стр.5] [стр.6] [стр.7] [стр.8] [стр.9] [стр.10] [стр.11] [стр.12] [стр.13] [стр.14] [стр.15] [стр.16] [стр.17] [стр.18] [стр.19] [стр.20] [стр.21] [стр.22] [стр.23] [стр.24] [стр.25] [стр.26] [стр.27] [стр.28] [стр.29] [стр.30] [стр.31] [стр.32] [стр.33] [стр.34] [стр.35] [стр.36] [стр.37] [стр.38] [стр.39] [стр.40] [стр.41] [стр.42] [стр.43] [стр.44] [стр.45] [стр.46] [стр.47] [стр.48] [стр.49] [стр.50] [стр.51] [стр.52] [стр.53] [стр.54] [стр.55] [стр.56] [стр.57] [стр.58] [стр.59] [стр.60] [стр.61] [стр.62] [стр.63] [стр.64] [стр.65] [стр.66] [стр.67] [стр.68] [стр.69] [стр.70] [стр.71] [стр.72] [стр.73] [стр.74] [стр.75] [стр.76] [стр.77] [стр.78] [стр.79] [стр.80] [стр.81] [стр.82] [стр.83] [стр.84] [стр.85] [стр.86] [стр.87] [стр.88] [стр.89] [стр.90] [стр.91] [стр.92] [стр.93] [стр.94] [стр.95] [стр.96] [стр.97] [стр.98] [стр.99] [стр.100] [стр.101] [стр.102] [стр.103] [стр.104] [стр.105] [стр.106] [стр.107] [стр.108] [стр.109] [стр.110] [стр.111] [стр.112] [стр.113] [стр.114] [стр.115] [стр.116] [стр.117] [стр.118] [стр.119] [стр.120] [стр.121] [стр.122] [стр.123] [стр.124] [стр.125] [стр.126] [стр.127] [стр.128] [стр.129] [стр.130] [стр.131] [стр.132] [стр.133] [стр.134] [стр.135] [стр.136] [стр.137] [стр.138] [стр.139] [стр.140] [стр.141] [стр.142] [стр.143] [стр.144] [стр.145] [стр.146] [стр.147] [стр.148] [стр.149] [стр.150] [стр.151] [стр.152] [стр.153] [стр.154] [стр.155] [стр.156] [стр.157] [стр.158] [стр.159] [стр.160] [стр.161] [стр.162] [стр.163] [стр.164] [стр.165] [стр.166] [стр.167] [стр.168] [стр.169] [стр.170] [стр.171] [стр.172] [стр.173] [стр.174] [стр.175] [стр.176] [стр.177] [стр.178] [стр.179] [стр.180] [стр.181] [стр.182] [стр.183] [стр.184] [стр.185] [стр.186] [стр.187] [стр.188] [стр.189] [стр.190] [стр.191] [стр.192] [стр.193] [стр.194] [стр.195] [стр.196]