Pull to refresh

GIMP Script-Fu Первый Дан. Язык функциональной геометрии. Итоги

Reading time8 min
Views736

Библиотека функций к 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)
"Рыба Питера Хендерсона загруженная из файла png"
"Рыба Питера Хендерсона загруженная из файла png"

Видно что "рыба" немного выходит за границы рамки плавником и щекой.

При демонстрации возможностей языка функциональной геометрии я пойду прямо по брошюре 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)

"Квадрат Эшера 1 и 2"
"Квадрат Эшера 1 и 2"
тестируем рисование квадрата Эшера.
;;(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))

"Квадрат Эшера 3 и 4"
"Квадрат Эшера 3 и 4"
Покрупнее.
(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")

"Квадрат Эшера 4"
"Квадрат Эшера 4"

Заключение

На этом я и завершаю описание восхождения на первую ступень мастерства в программировании на Scrip-fu в GIMP. Здесь я напомню какой путь мы прошли:

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

Конечно я осветил далеко не все темы программирования на Script-fu. За рамками моего повествования остались и интерактивная работа и работа с векторами(думаю уже многие знают что GIMP это уже давно не растровый редактор, а редактор сделавший серьёзную заявку на работу с векторной графикой) и различные тонкости использования множества функций GIMP и их комбинаций и многое многое другое. Но это была лишь только первая ступенька мастерства.

Tags:
Hubs:
Total votes 7: ↑7 and ↓0+10
Comments0

Articles