Библиотека функций к Script-fu
Язык функциональной геометрии
В предыдущей части мы рассмотрели базовые операции языка функциональной геометрии, так сказать кирпичики языка. Сегодня мы ознакомимся с более сложными функциями, т.е покажем что из этих кирпичиков можно построить. Базовым изображением для работы, будет "рыба" Питера Хендерсона.
Это svg файл и гимп может его использовать непосредственно:
Загрузка библиотек
(define path-home (getenv "HOME")) (define path-lib (string-append path-home "/work/gimp/lib/")) (define path-work (string-append path-home "/work/gimp/")) (load (string-append path-lib "util.scm")) (load (string-append path-lib "defun.scm")) (load (string-append path-lib "struct.scm")) (load (string-append path-lib "point.scm")) (load (string-append path-lib "tr2d.scm")) (load (string-append path-lib "contour.scm")) (load (string-append path-lib "img.scm")) (load (string-append path-lib "rect.scm")) (load (string-append path-lib "vect.scm")) (load (string-append path-lib "brush.scm")) (load (string-append path-lib "fig.scm")) (load (string-append path-lib "obj2.scm")) (load (string-append path-lib "fig-obj2.scm")) (load (string-append path-lib "pic.scm")) (define i1 (create-1-layer-img 640 480)) ;; создаём холст для рисования.
Создаём рисунок рыбы, загрузив её из файла.
(define i-fish (img-load (join-to-str path-work "fish.svg"))) (define len-fish (car (gimp-image-width i-fish))) (define fish-fig (image-fig! :image i-fish :flip #t :min-x 51 :max-x (- len-fish 51))) (define fish (make-picture fish-fig)) (define r1 (rect! (p! 100 100) (p! 100 0) (p! 0 -100))) (define c2 (make-rect-contour 0 0 50 50)) (define f2 (brush-fig! :name 'rect1 :color '(255 0 0) :contour c2)) (f2 :draw-to-rect i1 r1) (fish i1 r1)
Или можно использовать уже предварительно обработанный файл с "рыбой" png. Но для этого надо проделать конвертацию из svg в png.
конвертация рыбы svg в png
(define (save-png img path name) (gimp-file-save 1 img (car (gimp-image-active-drawable img)) (join-to-str path name) name)) (define i-fish (img-load (join-to-str path-work "fish.svg"))) (define fish-h (car (gimp-image-height i-fish))) (define fish-w (car (gimp-image-width i-fish))) (define i2 (create-transparent-layer-img (- fish-w 51) fish-h)) (define fish-fig (image-fig! :image i-fish :flip #f)) (define r2 (rect! (p! 0 fish-h) (p! fish-w 0) (p! 0 (- fish-h)))) (fish-fig :draw-to-rect i2 r2) (save-png i2 path-work "fish2.png")
теперь загрузим полученное изображение и проверим, что у нас получилось.
(define i-fish (img-load (join-to-str path-work "fish2.png"))) (define fish-fig (image-fig! :image i-fish :flip #f :min-x 51)) (define fish (make-picture fish-fig)) (define r1 (rect! (p! 100 100) (p! 100 0) (p! 0 -100))) (define c2 (make-rect-contour 0 0 50 50)) (define f2 (brush-fig! :name 'rect1 :color '(255 0 0) :contour c2)) (f2 :draw-to-rect i1 r1) (fish i1 r1)

Видно что "рыба" немного выходит за границы рамки плавником и щекой.
При демонстрации возможностей языка функциональной геометрии я пойду прямо по брошюре funcgeo2.pdf.
Двоечка.
Создадим простейшую комбинацию из рыб.
;;рамка для демонстрации габаритов. (define c2 (make-rect-contour 0 0 50 50)) (define f2 (brush-fig! :name 'rect1 :color '(255 0 0) :contour c2)) (define r1 (rect! (p! 100 110) (p! 100 0) (p! 0 -100))) (define r2 (rect! (p! 250 110) (p! 100 30) (p! 10 -100))) (f2 :draw-to-rect i1 r1) (f2 :draw-to-rect i1 r2) (define twice (pic-over fish (pic-rotate180 fish))) (twice i1 r1) (twice i1 r2)

Троечка.
Комбинация из трёх рыб.
;;рамка для демонстрации габаритов. (define c2 (make-rect-contour 0 0 50 50)) (define f2 (brush-fig! :name 'rect1 :color '(255 0 0) :contour c2)) (define r1 (rect! (p! 100 110) (p! 100 0) (p! 0 -100))) (define r2 (rect! (p! 250 110) (p! 100 30) (p! 10 -100))) (f2 :draw-to-rect i1 r1) (f2 :draw-to-rect i1 r2) (define fish2 (pic-flip (pic-rotate45 fish))) (define fish3 (pic-rotate270 fish2)) (define three (pic-over fish (pic-over fish2 fish3))) (three i1 r1) (three i1 r2)

Здесь интересно то, как мы "уменьшили" рыбу, повернув рисунок на 45 градусов.
Четвёрка.
Комбинация из четырёх рыб.
;;рамка для демонстрации габаритов. (define c2 (make-rect-contour 0 0 50 50)) (define f2 (brush-fig! :name 'rect1 :color '(255 0 0) :contour c2)) (define r1 (rect! (p! 100 110) (p! 100 0) (p! 0 -100))) (define r2 (rect! (p! 250 110) (p! 100 30) (p! 10 -100))) (f2 :draw-to-rect i1 r1) (f2 :draw-to-rect i1 r2) (define fish1 (pic-flip (pic-rotate45 fish))) (define fish2 (pic-rotate90 fish1)) (define fish3 (pic-rotate180 fish1)) (define fish4 (pic-rotate270 fish1)) (define four (pic-over fish1 fish2 fish3 fish4)) (four i1 r1) (four i1 r2)

Квартет
Функция объединяющая четыре рисунка.
(define-m (pic-quartet1 u1 u2 u3 u4) (pic-above (pic-beside u1 u2 0.5) (pic-beside u3 u4 0.5) 0.5)) ;;или можно реализовать на базе функции: ;; отобразить четыре изображения в рамке. ;; p1 p2 ;; p3 p4 (defun (pic-quartet p1 p2 p3 p4 &opt (kw 0.5) (kh 0.5)) (lambda (img dest-r) (let* ((r1 (rect! (rect-origin dest-r) ;; r3 r4 (vect-scale (rect-horiz dest-r) kw) ;; r1 r2 (vect-scale (rect-vert dest-r) kh))) (r2 (rect! (vect+ (rect-origin dest-r) (rect-horiz r1)) (vect-scale (rect-horiz dest-r) (- 1 kw)) (rect-vert r1))) (r3 (rect! (vect+ (rect-origin dest-r) (rect-vert r1)) (rect-horiz r1) (vect-scale (rect-vert dest-r) (- 1 kh)))) (r4 (rect! (vect+ (rect-origin r2) (rect-vert r2)) (rect-horiz r2) (rect-vert r3)))) (p1 img r3) (p2 img r4) (p3 img r1) (p4 img r2)))) (define-m (pic-quartet2 u1 u2 u3 u4) (pic-quartet u3 u4 u1 u2 0.5 0.5))
Функцию квартет можно выполнить в двух вариантах, в виде комбинации функций базовых примитивов языка функциональной геометрии, или в виде комбинации операций более низкого уровня, оперирующих векторами и рамками.
тестируем квартет
(define r1 (rect! (p! 50 210) (p! 200 0) (p! 0 -200))) (define r2 (rect! (p! 270 210) (p! 200 30) (p! 20 -200))) (define r3 (rect! (p! 50 430) (p! 200 0) (p! 0 -200))) (define r4 (rect! (p! 270 440) (p! 200 30) (p! 20 -200))) (f2 :draw-to-rect i1 r1) (f2 :draw-to-rect i1 r2) (f2 :draw-to-rect i1 r3) (f2 :draw-to-rect i1 r4) (define fish1 (pic-flip (pic-rotate45 fish))) (define fish2 (pic-rotate90 fish1)) (define fish3 (pic-rotate180 fish1)) (define fish4 (pic-rotate270 fish1)) (define four (pic-over fish1 fish2 fish3 fish4)) ((pic-quartet1 four four four four) i1 r1) ((pic-quartet four four four four 0.6 0.7) i1 r2) (begin (gimp-image-undo-disable i1) ((pic-quartet2 four four four four) i1 r3) ((pic-quartet2 four four four four) i1 r4) (gimp-image-undo-enable i1))
Даже такие простые функции как pic-quartet накапливают большую историю изменений, поэтому для увеличения скорости я блокирую накопление изменений.

Цикл
Изображение создаёт четыре исходных копии в рамке поделённой на 4 части, при этом копии поворачиваются на 90 градусов.
;; можно сделать как указано в брошюре ;; и у автора там кажется небольшая ошибка, он вращает фигуру не в ту сторону. ;;(define-m (pic-cycle p) ;; (pic-quartet p (pic-rotate90 p) (pic-rotate180 p) (pic-rotate270 p))) ;;вот так правильно. (define-m (pic-cycle p) (pic-quartet1 p (pic-rotate270 p) (pic-rotate90 p) (pic-rotate180 p))) ;;или можно реализовать на базе функции pic-embedded (defun (pic-cycle2 p &opt (kw 0.5) (kh 0.5)) (pic-quartet p (pic-rotate270 p) (pic-rotate90 p) (pic-rotate180 p) kw kh))
тестируем цикл
(define r1 (rect! (p! 50 210) (p! 200 0) (p! 0 -200))) (define r2 (rect! (p! 270 210) (p! 200 30) (p! 20 -200))) (define r3 (rect! (p! 50 430) (p! 200 0) (p! 0 -200))) (define r4 (rect! (p! 270 440) (p! 200 30) (p! 20 -200))) (f2 :draw-to-rect i1 r1) (f2 :draw-to-rect i1 r2) (f2 :draw-to-rect i1 r3) (f2 :draw-to-rect i1 r4) (define fish2 (pic-flip (pic-rotate45 fish))) (define fish3 (pic-rotate270 fish2)) (define three (pic-over fish (pic-over fish2 fish3))) ((pic-cycle (pic-rotate90 three)) i1 r1) ((pic-cycle2 (pic-rotate90 three) 0.6 0.7) i1 r2) (begin (gimp-image-undo-disable i1) ((pic-cycle2 (pic-rotate90 three)) i1 r3) ((pic-cycle2 (pic-rotate90 three)) i1 r4) (gimp-image-undo-enable i1))

Квартет циклов
Тут просто комбинация функций.
(define i1 (create-1-layer-img 740 480)) ;; создаём холст для рисования. (define r1 (rect! (p! 20 320) (p! 300 0) (p! 0 -300))) (define r2 (rect! (p! 370 330) (p! 300 30) (p! -30 -300))) (f2 :draw-to-rect i1 r1) (f2 :draw-to-rect i1 r2) (define v (pic-cycle (pic-rotate90 three))) (define cf1 (pic-quartet v v v v)) (begin (gimp-image-undo-disable i1) (cf1 i1 r1) (cf1 i1 r2) (gimp-image-undo-enable i1))

Собираем картинку Эшера.
Сразу поясню, это картинка состоящая из 9 различных картинок: одной- центральной, 4-х сторон и 4-х углов.
Сначала сформируем функцию рисования стороны.
;;функция создания "тройки" (define (pic-t-tile p1) (let* ((pt1 (pic-rotate45 (pic-rotate270 (pic-flip p1))))) (pic-over p1 (pic-over pt1 (pic-rotate270 pt1))))) ;;функция создания "четвёрки" (define (pic-u-tile p1) (let* ((pt1 (pic-rotate45 (pic-rotate270 (pic-flip p1)))) (pt2 (pic-over pt1 (pic-rotate90 pt1)))) (pic-over pt2 (pic-rotate180 pt2)))) (define (pic-side-n p1 n) (let ((pt1 (pic-t-tile p1))) (if (= n 0) (pic-empty) (pic-quartet (pic-side-n p1 (- n 1)) (pic-side-n p1 (- n 1)) (pic-rotate90 pt1) pt1))))
тестируем рисование стороны.
(define r1 (rect! (p! 50 210) (p! 200 0) (p! 0 -200))) (define r2 (rect! (p! 270 210) (p! 200 30) (p! 20 -200))) (f2 :draw-to-rect i1 r1) (f2 :draw-to-rect i1 r2) ;;((pic-side-n fish 0) i1 r1) ;; НИЧЕГО не рисует! ((pic-side-n fish 1) i1 r1) ((pic-side-n fish 2) i1 r2)

Теперь сформируем функцию рисования угла.
(define (pic-corner-n p1 n) (let ((pt1 (pic-u-tile p1))) (if (= n 0) (pic-empty) (pic-quartet (pic-corner-n p1 (- n 1)) (pic-side-n p1 (- n 1)) (pic-rotate90 (pic-side-n p1 (- n 1))) pt1))))
тестируем рисование угла.
(define r1 (rect! (p! 50 210) (p! 200 0) (p! 0 -200))) (define r2 (rect! (p! 270 210) (p! 200 30) (p! 20 -200))) (f2 :draw-to-rect i1 r1) (f2 :draw-to-rect i1 r2) ;;((pic-corner-n fish 0) i1 r1) ;; НИЧЕГО не рисует! ((pic-corner-n fish 1) i1 r1) ((pic-corner-n fish 2) i1 r2)

И заключительные функции рисования квадрата Эшера.
;;рисунок из 9 рисунков ;; p1 p2 p3 ;; p4 p5 p6 ;; p7 p8 p9 (define-m (pic-nonet p1 p2 p3 p4 p5 p6 p7 p8 p9) (let* ((k1 (/ 1 3))) ;;(print "make pic-nonet") (lambda (img dest-r) (let* ((v-h (vect-scale (rect-horiz dest-r) k1)) (v-v (vect-scale (rect-vert dest-r) k1)) (r1 (rect! (rect-origin dest-r) v-h v-v)) (r2 (rect! (vect+ (rect-origin dest-r) v-h) v-h v-v)) (r3 (rect! (vect+ (rect-origin r2) v-h) v-h v-v)) (r4 (rect! (vect+ (rect-origin r1) v-v) v-h v-v)) (r5 (rect! (vect+ (rect-origin r2) v-v) v-h v-v)) (r6 (rect! (vect+ (rect-origin r3) v-v) v-h v-v)) (r7 (rect! (vect+ (rect-origin r4) v-v) v-h v-v)) (r8 (rect! (vect+ (rect-origin r5) v-v) v-h v-v)) (r9 (rect! (vect+ (rect-origin r6) v-v) v-h v-v))) (p1 img r7) (p2 img r8) (p3 img r9) (p4 img r4) (p5 img r5) (p6 img r6) (p7 img r1) (p8 img r2) (p9 img r3))))) (define (pic-square-limit-n p1 n) (if (= n 0) (pic-empty) (let ((pt1 (pic-u-tile p1)) (pt2 (pic-corner-n p1 (- n 1))) (pt3 (pic-side-n p1 (- n 1)))) (pic-nonet pt2 pt3 (pic-rotate270 pt2) (pic-rotate90 pt3) pt1 (pic-rotate270 pt3) (pic-rotate90 pt2) (pic-rotate180 pt3) (pic-rotate180 pt2)))))
тестируем рисование квадрата Эшера.
;;(define i1 (create-1-layer-img 740 480)) ;; создаём холст для рисования. (define r1 (rect! (p! 20 320) (p! 300 0) (p! 0 -300))) (define r2 (rect! (p! 370 330) (p! 300 30) (p! -30 -300))) (f2 :draw-to-rect i1 r1) (f2 :draw-to-rect i1 r2) ((pic-square-limit-n fish 1) i1 r1) ((pic-square-limit-n fish 2) i1 r2)

тестируем рисование квадрата Эшера.
;;(define i1 (create-1-layer-img 740 480)) ;; создаём холст для рисования. (define r1 (rect! (p! 20 320) (p! 300 0) (p! 0 -300))) (define r2 (rect! (p! 370 330) (p! 300 30) (p! -30 -300))) (f2 :draw-to-rect i1 r1) (f2 :draw-to-rect i1 r2) (begin (gimp-image-undo-disable i1) ((pic-square-limit-n fish 3) i1 r1) ((pic-square-limit-n fish 4) i1 r2) (gimp-image-undo-enable i1))

Покрупнее.
(define i1 (create-1-layer-img 500 500)) ;; создаём холст для рисования. (define r1 (rect! (p! 0 500) (p! 500 0) (p! 0 -500))) (f2 :draw-to-rect i1 r1) (begin (gimp-image-undo-disable i1) ((pic-square-limit-n fish 4) i1 r1) (gimp-image-undo-enable i1)) (save-png i1 path-work "square-limit-4.png")

Заключение
На этом я и завершаю описание восхождения на первую ступень мастерства в программировании на Scrip-fu в GIMP. Здесь я напомню какой путь мы прошли:
Шаг первый
Первое приветствие
Работа, Печать, Отладка
Макросы. Первое знакомство
Расширения к Script-fu
Погружение в программирование графики
Точки, Контуры, Кисти и Градиенты
Сортировка
Ускоряем Script-fu
Наивные графические преобразования
Реализация Хеш-Таблицы
Линейные преобразования на плоскости
Удобная передача параметров в функцию
Берём Кисти и рисуем Точки и Звёздочки
Рамки
Фигуры
Объектно-ориентрованное программирование в Scheme. Простая реализация
Фигуры. Объектный подход
Выходим за Рамки
Язык функциональной геометрии. Рисунки(картинки) и базовые операции
Язык функциональной геометрии. Итоги.
Конечно я осветил далеко не все темы программирования на Script-fu. За рамками моего повествования остались и интерактивная работа и работа с векторами(думаю уже многие знают что GIMP это уже давно не растровый редактор, а редактор сделавший серьёзную заявку на работу с векторной графикой) и различные тонкости использования множества функций GIMP и их комбинаций и многое многое другое. Но это была лишь только первая ступенька мастерства.
