Напомню нашу цель, это создание языка функциональной геометрии. Одно из базовых операций в нём является отрисовка некоторого изображения в заданный прямоугольник, вернее даже не прямоугольник, а рамку, которая может представлять из себя параллелограмм, повёрнутый на произвольный угол относительно горизонтальной оси координат, расположенный в любой точке плоскости рисования. Пока мы и близко не приблизились к этой цели. Продолжим исследование возможностей GIMPа по отображению изображений.
Библиотека функций к Script-fu
подготовка
(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 "img.scm"))
(load (string-append path-lib "struct.scm"))
(load (string-append path-lib "point.scm")) ;;там операции с углами.
Напишем первую функцию:
;;установили цвет бекграунда
(define i1 (create-1-layer-img 640 480))
(define isource1 (car (file-png-load 1
(string-append path-work "i1.png") "i1")))
(define (draw-from-image1 dest dest-x dest-y src)
(let ((dw2 (car (gimp-layer-new-from-drawable
(car (gimp-image-get-active-layer src)) dest))))
(gimp-image-add-layer dest dw2 0)
(gimp-layer-set-offsets dw2 dest-x dest-y)
(gimp-image-merge-visible-layers dest CLIP-TO-IMAGE)))
(draw-from-image1 i1 10 50 isource1)
С помощью этой функции мы можем разместить любое загруженное изображение на редактируемом изображении в указанной позиции.
Целевой прямоугольник может не совпадать, с размером изображения, поэтому мы должны уметь масштабировать размещаемое изображение. Напишем вторую функцию:
(define (draw-from-image-scale dest dest-x dest-y src sc-x sc-y)
(let* ((dw2 (car (gimp-layer-new-from-drawable
(car (gimp-image-get-active-layer src)) dest)))
(h (car (gimp-drawable-height dw2)))
(w (car (gimp-drawable-width dw2))))
(gimp-image-add-layer dest dw2 0)
(gimp-layer-scale dw2 (* w sc-x) (* h sc-y) TRUE)
(gimp-layer-set-offsets dw2 dest-x dest-y)
(gimp-image-merge-visible-layers dest CLIP-TO-IMAGE)))
(draw-from-image-scale i1 10 50 isource1 0.5 3)
(draw-from-image-scale i1 150 50 isource1 2.5 1.4)
(draw-from-image-scale i1 320 50 isource1 0.9 1.2)
Повороты
А теперь разберёмся с поворотами. Изображения нужно не только сдвигать и масштабировать, но и поворачивать.
(define (draw-from-image-rotate dest dest-x dest-y src sc-x sc-y alfa)
(let* ((dw2 (car (gimp-layer-new-from-drawable
(car (gimp-image-get-active-layer src)) dest)))
(h (car (gimp-drawable-height dw2)))
(w (car (gimp-drawable-width dw2)))
(delta 0.001)
(alfa-r (gr2rad alfa)))
(gimp-image-add-layer dest dw2 0)
(if (or (> (abs (- sc-x 1)) delta)
(> (abs (- sc-y 1)) delta))
(gimp-layer-scale dw2 (* w sc-x) (* h sc-y) TRUE))
(if (> (abs alfa-r) delta)
(gimp-drawable-transform-rotate dw2 alfa-r FALSE 0 0
TRANSFORM-FORWARD
INTERPOLATION-LINEAR
FALSE 1
TRANSFORM-RESIZE-ADJUST))
(gimp-layer-set-offsets dw2 dest-x dest-y)
(gimp-image-merge-visible-layers dest CLIP-TO-IMAGE)))
По функции видно последовательность действий: сначала масштабируем, потом поворачиваем
Как вам? По мне так получилось - не очень! Точка начала вставляемого рисунка уехала при повороте! Это надо исправить! Пишем второй вариант.
;; что то вроде остатка по модулю от 360
(define (angle-360 angle)
(cond
((>= angle 360) (angle-360 (- angle 360)))
((< angle 0) (angle-360 (+ angle 360)))
(#t angle)))
;; непосредственно функция вычисления смещения, которое зависит от величины угла поворота.
(define (calc-offset-for-rotate alfa w h)
(let* ((a (angle-360 alfa))
(a-r (gr2rad a)))
(cond
((<= a 90)
(cons (- (* h (sin a-r))) 0))
((<= a 180)
(cons (- (- (* h (sin a-r))
(* w (cos a-r))))
(* h (cos a-r))))
((<= a 270)
(cons (* w (cos a-r))
(+ (* w (sin a-r))
(* h (cos a-r)))))
((<= a 360)
(cons 0
(* w (sin a-r))))
)))
(define (draw-from-image-rotate dest dest-x dest-y src sc-x sc-y alfa)
(gimp-image-undo-disable dest)
(let* ((dw2 (car (gimp-layer-new-from-drawable
(car (gimp-image-get-active-layer src)) dest)))
(h (car (gimp-drawable-height dw2)))
(w (car (gimp-drawable-width dw2)))
(delta 0.001)
(alfa-r (gr2rad alfa)))
(gimp-image-add-layer dest dw2 0)
(if (or (> (abs (- sc-x 1)) delta)
(> (abs (- sc-y 1)) delta))
(gimp-layer-scale dw2 (* w sc-x) (* h sc-y) TRUE))
(if (> (abs alfa-r) delta)
(set! dw2 (car (gimp-drawable-transform-rotate dw2 alfa-r FALSE 0 0
TRANSFORM-FORWARD
INTERPOLATION-LINEAR
FALSE 1
TRANSFORM-RESIZE-ADJUST))))
(let* ((deltas (calc-offset-for-rotate alfa (* w sc-x) (* h sc-y)))
)
(gimp-layer-set-offsets dw2
(+ dest-x (car deltas))
(+ dest-y (cdr deltas))))
(gimp-image-merge-visible-layers dest CLIP-TO-IMAGE))
(gimp-image-undo-enable dest))
Здесь я применяю ещё и отмену от создания списка отката редактирования, уж больно большие списки отката создаются, но делать это не обязательно.
Выполняем тестирование.
(draw-from-image-rotate i1 100 200 isource1 1.0 1.0 0)
(draw-from-image-rotate i1 100 200 isource1 1.0 1.0 30)
(draw-from-image-rotate i1 100 200 isource1 1.0 1.0 45)
(draw-from-image-rotate i1 100 200 isource1 1.0 1.0 90)
Давайте создадим на основе этой функции другую, создающую вращение изображений вокруг заданного центра.
(define (rot-img1 dest src x y scale num)
(let ((a1 (floor (/ 360 num)))
(a-cur 0))
(while (< a-cur 360)
(draw-from-image-rotate dest x y src scale scale a-cur)
(set! a-cur (+ a-cur a1)))))
(rot-img1 i1 isource1 300 200 0.5 5)
(rot-img1 i1 isource1 100 100 0.3 4)
При построении функции draw-from-image-rotate
мы использовали три функции GIMP: gimp-layer-scale
, gimp-drawable-transform-rotate
, gimp-layer-set-offsets
, это получилось в следствии того что к её окончательному виду мы подходили постепенно, эволюционно, задействуя средства ГИМПа по мере необходимости. GIMP же в свою очередь имеет функцию, позволяющую заменить все эти три функции одной:
;; новый подход через функции трансформации
(define (draw-from-image-rotate2 dest dest-x dest-y src sc-x sc-y alfa)
(let* ((dw2 (car (gimp-layer-new-from-drawable
(car (gimp-image-get-active-layer src)) dest)))
(h (car (gimp-drawable-height dw2)))
(w (car (gimp-drawable-width dw2)))
(delta 0.001)
(alfa-r (gr2rad alfa)))
(gimp-image-add-layer dest dw2 0)
(gimp-item-transform-2d dw2 0 0 sc-x sc-y alfa-r dest-x dest-y)
(gimp-image-merge-visible-layers dest CLIP-TO-IMAGE)
))
Протестируем её с помощью функции кратного вращения изображения.
(define (rot-img2 dest src x y scale num)
(gimp-image-undo-group-start dest)
(let ((a1 (floor (/ 360 num)))
(a-cur 0))
(while (< a-cur 360)
(draw-from-image-rotate2 dest x y src scale scale a-cur)
(set! a-cur (+ a-cur a1))))
(gimp-image-undo-group-end dest))
(rot-img2 i1 isource1 250 200 0.5 14)
(rot-img2 i1 isource1 450 200 0.4 5)
Как видите GIMP имеет огромное разнообразие функций позволяющих прийти к одному результату разными путями. Далее мы рассмотрим использование функции трансформации изображений позволяющую заменить весь набор рассмотренных ранее, тех которые рассмотрены не были, настоящую киллер функцию, остальных функции преобразования, с небольшой поправочкой - линейных преобразований.
Но прежде чем перейти к её рассмотрению, давайте ещё немного расширим возможности языка тинисхема, о чём и будет следующая статья.
BigBeaver
А какие-то аналоги типа CFDG рассматривались для этой задачи, или она скорее академический характер имеет?
IisNuINu Автор
Спасибо за комментарий, CFDG не рассматривал, просто я делал упражнение из SICP, поэтому и выбрал этот язык, а CFDG, насколько я понял возник гораздо позже. Но идея его реализовать в GIMP весьма интересная. Я подумаю, может быть может быть и его реализую.