Библиотека функций к Script-fu
Мы уже использовали контуры, для вычерчивания кривых линий, для формирования выделений, для закрашивания областей изображения. Но абстракция контура охватывает только позиции(точки), которые можно использовать по своему усмотрению. Пора бы создать абстракцию которая бы объединила возможность рисования контура, кисть, цвет, и тип получаемого изображения, будь это фигура или голый контур. Руководствуясь этими соображениями я объединил в структуре фигуры и окружил её набором функций, создающих абстракцию фигуры.
;;фигура это контур, соединённый с цветом и кистью, которой этот контур надо рисовать.
;;вспомогательные поля габаритов рассчитываются в функциях инициализации, чтобы не рассчитывать их
;;в дальнейшем. при трансформациях это надо учитывать.
;;==========================================================================================
(struct fig (name type color brush contour min-x min-y max-x max-y))
;;==========================================================================================
Набор функций создающих фигуры:
(define default-color '(127 127 127))
(define default-brush (make-brush2 "Circle (05)"))
;; эта процедура не для пользователя а для программ
(define (make-fig contour type name color brush)
(let ((min-p (min-pos contour))
(max-p (max-pos contour)))
(fig! name type color brush
contour
(p-x min-p) (p-y min-p)
(p-x max-p) (p-y max-p))))
;;создаёт обычную фигуру-контур отрисовываемый цветом и кистью
(defun (make-brush-fig contour &key :name :color :brush)
(make-fig contour 'brush name
(if (not color)
default-color
color)
(if (not brush)
default-brush
brush)))
;;создаёт фигуру заполняемую заданным цветом.
(defun (make-shape-fig contour &key :name :color :brush)
(make-fig contour 'shape name
(if (not color)
default-color
color)
(if (not brush)
default-brush
brush)))
;;создаёт фигуру контур отрисовываемый карандашом - pеncil
(defun (make-pencil-fig contour &key :name :color :brush)
(make-fig contour 'pencil name
(if (not color)
default-color
color)
(if (not brush)
default-brush
brush)))
Мы будем создавать фигуры с несколькими типами использования контуров, т.е их отображения: контур, изображаемый с помощью кисти, с помощью карандаша и заполняемый определённым цветом.
;;вынесем функционал формирования массива из списка точек в отдельную функцию
(define (make-contour-vector contour num-points)
(let ((points (make-vector (* 2 num-points) 'double))
(i 0)
(cur contour))
(while (< i num-points)
(vector-set! points (* 2 i) (p-x (car cur)))
(vector-set! points (+ (* 2 i) 1) (p-y (car cur)))
(set! i (+ i 1))
(set! cur (cdr cur)))
points))
;;функция отображения фигуры fig на изображении img с помощью трансформации tr
(define (draw-fig img fig tr)
(let* ((contour (if tr
(translate-contour (fig-contour fig) tr)
(fig-contour fig)))
(num-points (length contour))
(points (make-contour-vector contour num-points))
(dw (car (gimp-image-get-active-drawable img))))
;;(gimp-context-push)
(gimp-context-set-foreground (fig-color fig))
(cond
((eq? (fig-type fig) 'pencil)
((fig-brush fig))
(gimp-pencil dw (* 2 num-points) points)
((eq? (fig-type fig) 'brush)
((fig-brush fig))
(gimp-paintbrush-default dw (* 2 num-points) points))
((eq? (fig-type fig) 'shape)
;;(gimp-image-select-polygon img CHANNEL-OP-REPLACE num-points points)
(gimp-free-select img (- (* 2 num-points) 1) points CHANNEL-OP-REPLACE 0 0 0)
(gimp-edit-fill dw FOREGROUND-FILL)
(gimp-selection-none img))
)
;;(gimp-context-pop)
))
Подготовим скрипт-фу к работе, загрузив необходимые библиотеки.
для 2.10
(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"))
для 2.6
(define path-home "D:") ;;для виндовс
;;(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 "img2.6.scm"))
(load (string-append path-lib "rect.scm"))
(load (string-append path-lib "vect.scm"))
(load (string-append path-lib "brush-2.6.scm"))
(load (string-append path-lib "fig2.6.scm"))
Выполним тест, на двух фигурах
(define i1 (create-1-layer-img 640 480)) ;;подготовим полотно для рисования.
(define c1 (make-circle-n 50 50 50 10))
(define f1 (make-brush-fig c1 :name 'circle1))
(define f2 (make-pencil-fig c1 :name 'circle2 :color '(255 255 255)))
;;(define f2 (make-pencil-fig c1 :name 'circle2 :color '(0 255 255)))
(define tr3 (comb-tr2d
(make-tr2d-scale 2 1.5)
(make-tr2d-shear-y 15)
(make-tr2d-rot -90)
(make-tr2d-move 100 300)))
(draw-fig i1 f1 #f)
(draw-fig i1 f2 tr3)
Теперь рисуем фигуру заполняемую цветом
(define f3 (make-shape-fig c1 :name 'circle2 :color '(255 255 0)))
(define tr4 (comb-tr2d
(make-tr2d-scale 1.5 1.5)
(make-tr2d-shear-y 0)
(make-tr2d-rot 30)
(make-tr2d-move 350 50)))
(draw-fig i1 f3 tr4)
(define tr5 (comb-tr2d
(make-tr2d-scale 1. 1.)
(make-tr2d-shear-x 20)
(make-tr2d-rot 0)
(make-tr2d-move 90 100)))
(draw-fig i1 f3 tr5)
Рисование фигуры означает использование одного контура, что сильно обедняет наши возможности по созданию изображений. А вот если объединить фигуры в список, то эта структура позволит создавать практически любое изображение. Для реализации данной функциональности нет необходимости создавать отдельную абстракцию. достаточно создать функцию отображающую подобную структуру. Причём данная функция использует не трансформацию tr2d, для получения изображения, а рамку(frame), на основании которой она сама рассчитывает трансформацию и применяет ко всем входящим в неё фигурам.
(define (draw-figs img list-fig r)
(let* ((delta 0.00001)
(p-min (min-pos-figs list-fig))
(p-max (max-pos-figs list-fig))
(tr (make-tr2d-from-rect r
(- (p-x p-max) (p-x p-min))
(- (p-y p-max) (p-y p-min))))
(dw (car (gimp-image-get-active-drawable img))))
;;(gimp-context-push)
(if (or (> (abs (p-x p-min)) delta)
(> (abs (p-y p-min)) delta));;начало фигуры сдвинуто относительно
(set! tr (comb-tr2d ;;начала координат надо подвинуть туда!
(make-tr2d-move (- (p-x p-min)) (- (p-y p-min)))
tr)))
(do ((cur list-fig (cdr cur)))
((null? cur) list-fig)
(let ((cur-fig (car cur)))
(draw-fig img cur-fig tr)
))
;;(gimp-context-pop)
))
(define (min-pos-figs list-fig)
(let ((min-x maxnum)
(min-y maxnum))
(do ((cur list-fig (cdr cur)))
((null? cur) (p! min-x min-y))
(let ((cur-p (car cur)))
(if (< (fig-min-x cur-p) min-x)
(set! min-x (fig-min-x cur-p)))
(if (< (fig-min-y cur-p) min-y)
(set! min-y (fig-min-y cur-p)))
)
)))
(define (max-pos-figs list-fig)
(let ((max-x minnum)
(max-y minnum))
(do ((cur list-fig (cdr cur)))
((null? cur) (p! max-x max-y))
(let ((cur-p (car cur)))
(if (> (fig-max-x cur-p) max-x)
(set! max-x (fig-max-x cur-p)))
(if (> (fig-max-y cur-p) max-y)
(set! max-y (fig-max-y cur-p)))
))))
Давайте проверим работоспособность этой функции и её функциональность.
(define bsh1 (make-brush1 :name "2. Hardness 075" :size 20))
(define bsh2 (make-brush1 :name "2. Hardness 025" :size 10))
;;2.6
;;посмотрим какие кисти есть.
;;(gimp-brushes-get-list ".*")
;;Circle Fuzzy (11)
(define bsh1 (make-brush1 "Circle Fuzzy (17)" :name "My Brush1" :radius 20))
(define bsh2 (make-brush2 "My Brush1" :radius 10 :aspect-ratio 0.5))
;; рисуем обычную звезду.
(define c2 (translate-contour (make-star 50 5 0.3)
(make-tr2d-rot -90)))
(define p1n (min-pos c2))
(define p1x (max-pos c2))
(define c2o (translate-contour c2
(make-tr2d-move (abs (p-x p1n)) (abs (p-y p1n)))))
(define f2 (make-shape-fig c2o :name 'star1 :color '(127 127 0)))
(define f3 (make-brush-fig c2o :name 'star2 :color '(127 0 255) :brush bsh1))
;;(define f4 (make-pencil-fig c2o :name 'star3 :color '(0 0 255) :brush bsh2))
(define f4 (make-brush-fig c2o :name 'star3 :color '(255 0 0) :brush bsh2))
(define fs1 (list f2 f3 f4))
(define r1 (make-rect-by-vect (p! 100 50) (p! 200 0) (p! 0 200)))
(draw-figs i1 fs1 r1)
Я немного поменял фон, так что изображение получилось вот такое.
Прежде чем рассмотреть ещё один пример создания комплексной(составной) фигуры, создадим функцию создания стрелок.
Стрелка.
Создать контур стрелки довольно легко. Во первых контур рисуется из начала координат, а сама стрелка представляет из себя вектор обозначаемый точкой.
(define tr-s-arrow (make-tr2d-scale (- (/ 1 10)) (- (/ 1 10))))
(define tr-up-arrow (make-tr2d-rot 15))
(define tr-down-arrow (make-tr2d-rot (- 15)))
(define (make-arrow1 target)
(let* ((p2 (p-tr2d target tr-s-arrow))
(p3 (p-tr2d p2 tr-down-arrow))
(p4 (p-tr2d p2 tr-up-arrow))
(tr-s1 (make-tr2d-move (p-x target) (p-y target)))
(p3a (p-tr2d p3 tr-s1))
(p4a (p-tr2d p4 tr-s1)))
(list (p! 0 0) target p3a p4a target)))
Данный контур имеет острие равное десятой части полной длины стрелки, и угол заострения 30 градусов.
Тестируем:
;;контур прямоугольника
(define c8 (make-rect-contour 0 0 50 50))
;;контур стрелки сдвинутой к середине прямоугольника.
(define c9 (translate-contour (make-arrow1 (p! 0 50))
(make-tr2d-move 25 0)))
;;2.10
(define bsh4 (make-brush1 :name "2. Hardness 075" :size 5))
;;2.6
(define bsh4 (make-brush2 "My Brush1" :radius 2))
;;составная фигура из прямоугольника, заливки и стрелки.
(define fs3
(list
(make-shape-fig c8 :color '(255 255 0))
(make-pencil-fig c8 :color '(0 255 0) :brush bsh4)
(make-pencil-fig c9 :color '(255 0 0) :brush bsh4)))
;; рисуем в 3х разных рамках, рамки получаем трансляцией базовой рамки 100х100
(draw-figs i1 fs3 (rect! (p! 0 0) (p! 100 0) (p! 0 100)))
(draw-figs i1 fs3
(rect-tr2d base-rect
(comb-tr2d
(make-tr2d-scale 100 50)
(make-tr2d-rot 45)
(make-tr2d-move 300 200))))
(draw-figs i1 fs3
(rect-tr2d base-rect
(comb-tr2d
(make-tr2d-scale 100 100)
(make-tr2d-shear-x -40)
(make-tr2d-shear-y 10)
(make-tr2d-rot -45)
(make-tr2d-move 75 335))))
Итого
Мы создали абстракцию позволяющую строить фигуры созданные на основе контуров различного цвета, и рисуемые с помощью различных кистей.