How to export flattened image with GIMP Script-Fu - scheme

I've got a script that's supposed to flatten, resize and export an initial image. I'm stuck on the export. I'm using file-png-save. It seems fine with my parameters except for the third parameter, which is supposed to be a drawable.
For the drawable, I'm using the flattened layer I got from gimp-image-flatten. I'm getting a response that my third argument is invalid. Do layers not always work as drawables? Do I need to convert it?
(define (script-fu-panel-export inImg drawable inWidth)
(define filename (car(gimp-image-get-filename inImg)))
(define comHeight (/ inWidth .75))
(define piece (car(cdr(cdr(cdr(cdr(cdr(strbreakup filename "/"))))))))
(define base (car(strbreakup piece ".")))
(define destination (string-append "/home/samjones/Dev/mobinge/lib/images/" base "-" (number->string inWidth) ".png"))
(let* ((duplicateImg (car(gimp-image-duplicate inImg))))
(gimp-image-scale duplicateImg inWidth comHeight)
(let* ((flatLayer (gimp-image-flatten duplicateImg)))
(file-png-save 1 duplicateImg flatLayer destination destination 1 0 0 0 0 0 0)
)
(gimp-display-new duplicateImg)
)
)
(script-fu-register
"script-fu-panel-export"
"Export Panel. . ."
"Creates a flattened image export to a selected size.."
"Sam Jones"
"copyright 2017, Sam Jones"
"December 19, 2017"
""
SF-IMAGE "Image" 0
SF-DRAWABLE "Maybe unused" 0
SF-ADJUSTMENT "Width" '(320 20 1200 10 50 0 SF-SLIDER)
)
(script-fu-menu-register "script-fu-panel-export" "<Image>/Filters")

Looking around for people who save from scripts, I found a slightly different route that worked. I replaced these two lines:
(let* ((flatLayer (gimp-image-flatten duplicateImg)))
(file-png-save 1 duplicateImg flatLayer destination destination 1 0 0 0 0 0 0))
With this:
(gimp-image-flatten duplicateImg)
(file-png-save 1 duplicateImg (car(gimp-image-get-active-drawable duplicateImg))_destination destination 1 0 0 0 0 0 0)
So I think gimp-image-flatten probably returns a list with the layer as its first element instead of returning a layer. I now know that gimp-image-get-active returns a list with the element.
It's weird, but it works.

You are not accessing the desired element of the result of gimp-image-flatten. Use car to access it:
(let* ((flatLayer (car (gimp-image-flatten duplicateImg))))
(file-png-save 1 duplicateImg flatLayer destination destination 1 0 0 0 0 0 0)
)

Related

unpacking binary file via octets->string->unpack fails: signed int `#(243 0)` is illegal UTF8

I am parsing a binary file (nifti) with a mix of chars, floats, ints, and shorts (using the PDL::IO::Nifti cpan module as reference).
I am having some luck parsing sequences of octets to a string so they can be passed to cl-pack:unpack. This is convoluted but convenient for porting using the perl module as reference.
This strategy fails on reading #(243 0) as binary
(setf my-problem (make-array 2
:element-type '(unsigned-byte 8)
:initial-contents #(243 0)))
(babel:octets-to-string my-problem)
Illegal :UTF-8 character starting at position 0
and, when trying to read the file as char*
the octet sequence #(243 0 1 0) cannot be decoded.
I'm hoping there is a simple encoding issue I haven't figured out. Trying to go in the reverse direction (packing 243 and getting octets) gives a vector of length 3 for what I expect to be 2.
(babel:string-to-octets (cl-pack:pack "s" 243))
; yields #(195 179 0) expect #(243 0)
Full context
;; can read up to position 40. at which we expect 8 signed ints.
;; 4th int is value "243" but octet cannot be parsed
(setq fid-bin (open "test.nii" :direction :input :element-type 'unsigned-byte))
(file-position fid-bin 40)
(setf seq (make-array (* 2 8) :element-type '(unsigned-byte 8)))
(read-sequence seq fid-bin)
; seq: #(3 0 0 1 44 1 243 0 1 0 1 0 1 0 1 0)
(babel:octets-to-string seq) ; Illegal :UTF-8 character starting at position 6.
(sb-ext:octets-to-string seq) ; Illegal ....
;; first 3 are as expected
(cl-pack:unpack "s3" (babel:octets-to-string (subseq seq 0 6)))
; 3 256 300
(setf my-problem (subseq seq 6 8)) ; #(243 0)
(babel:octets-to-string my-problem) ; Illegal :UTF-8 character starting at position 0.
;; checking the reverse direction
;; 243 gets represented as 3 bytes!?
(babel:string-to-octets (cl-pack:pack "s3" 3 256 300)) ; #(3 0 0 1 44 1)
(babel:string-to-octets (cl-pack:pack "s4" 3 256 300 243)) ; #(3 0 0 1 44 1 195 179 0)
(setq fid-str (open "test.nii" :direction :input))
(setf char-seq (make-array (* 2 8) :initial-element nil :element-type 'char*))
(file-position fid-str 40)
(read-sequence char-seq fid-str)
;; :UTF-8 stream decoding error on #<SB-SYS:FD-STREAM ....
;; the octet sequence #(243 0 1 0) cannot be decoded.
The perl equivalent
open my $f, "test.nii";
seek $f, 46, 0;
read $f,my $b, 2;
print(unpack "s", $b); # 243
The problem is that you are using functions which try to treat some sequence of octets as a representation of an encoding of a sequence of characters (or of some Unicode things: I think there are things other than characters in Unicode). In particular, in your case, the functions you are using are treating a sequence of octets as the UTF-8 encoding of some string. Well, not all sequences of octets are legal UTF-8 so the functions are, correctly, puking on an illegal sequence of octets.
But that's because you're not doing the right thing: what you want to do is to take a sequence of octets and make a string whose char-codes are those octets. You don't want to be doing with any silly encoding-big-characters-in-small-integers rubbish, because you will never see any big characters. You want something like these functions (both somewhat misnamed, since they aren't fussed about the whole octet thing unless you are).
(defun stringify-octets (octets &key
(element-type 'character)
(into (make-string (length octets)
:element-type element-type)))
;; Smash a sequence of octets into a string.
(map-into into #'code-char octets))
(defun octetify-string (string &key
(element-type `(integer 0 (,char-code-limit)))
(into (make-array (length string)
:element-type element-type)))
;; smash a string into an array of 'octets' (not actually octets)
(map-into into #'char-code string))
And now you can check everything works:
> (octetify-string (pack "s" 243))
#(243 0)
> (unpack "s" (stringify-octets (octetify-string (pack "s" 243))))
243
and so on. Given your example sequence:
> (unpack "s8" (stringify-octets #(3 0 0 1 44 1 243 0 1 0 1 0 1 0 1 0)))
3
256
300
243
1
1
1
1
A really much better approach would be to have the packing & unpacking functions simply handle sequences of octets. But I suspect that's a lost cause. An interim approach which is horrible but less horrible than converting sequences of octets to characters would be to read the file as text but with an external-format which does no translation at all. How to do that is implementation-dependent (but something based on latin-1 will be a good start).
It seems that the problem is indeed encoding-related:
CL-USER> (cl-pack:pack "s" 243)
"รณ\0"
which is the same as the result of:
(babel:octets-to-string my-problem :encoding :iso-8859-1)

Adding parenthesis changes evaluation of condition

I'm starting to learn Scheme and stumbled on this curious thing:
Given the following procedure:
(define (test x y) (if (= x 0) 0 y ))
When I create a conditional, it evaluates "as expected" when I add parenthesis to it: (test 0 1) gives 0. But when I don't add parenthesis (and I use the same input) it evaluates to the false condition: test 0 1 gives 1.
Why is this?
If you write:
test 0 1
This is the same as:
(begin
test ; evaluate variable test => #<procedure:something...> or similar value
0 ; evaluate literal 0 => 0
1) ; evaluate literal 1 => 1
==> 1 ; because 1 is the last expression in the block it is the result of it.
When you do (test 0 1) you are calling the procedure you'll get by evaluating variable test with the two arguments 0 and 1 which gets evaluated to the numbers they represent. If you do substitution it becomes:
(if (= 0 0) ; if 0 is the same numeric value as 0
0 ; then evaluate 0
1) ; else evaluate 1
==> 0
The same in JavaScript:
const test = (x, y) => x === 0 ? 0 : y;
test;0;1
==> 1 // result in node is 1 since it was the last expression
test(0, 1); // calling the fucntion behind the variable test with the arguments 0 and 1
==> 0
So parentheses matters around stuff as they matter afdter stuff in JavaScript. Basically (one two three) in Scheme is one(two, three) in JS. Just adding parentheses around somtheing is to just add () after something in JS.

text aligning by dxf groups

I have a bunch of text lines , iam drawing\showing them one in each line using entmake procedure. and for that i provide a insertion point , i want the text to be aligned from the left , the problem is that the lines lengths are different and the insertion point seems to be the center of text.
i was thinking of using the length of the text and the size\height of the text to calculate the pad i need to make so the text is aligned . if iam in the right path i cant find out how to do the calculation .
if I am not please help.
here is how i make text entities:
(defun text(point text)
(list ( cons 0 "TEXT")
(cons 11 point)
(cons 10 point)
(cons 40 0.4)
(cons 1 text)
(cons 41 1.0)
(cons 72 4)
(cons 73 0)
) )
thank you
You can create left-justified single-line TEXT entities using the following entmakex expression:
(defun mytext ( ins hgt str )
(entmakex
(list
'(000 . "TEXT")
(cons 010 ins)
(cons 040 hgt)
(cons 001 str)
)
)
)
Which may be called for example:
(mytext '(1.0 1.0 0.0) 0.4 "This is a test")
Here:
DXF group 0 is the entity type
DXF group 10 is the text insertion point
DXF group 40 is the text height
DXF group 1 is the text content
These four DXF groups are the minimum groups required to create a single-line TEXT entity.
For left-justified single-line text, only DXF group 10 is required to specify the position; for all other justifications, DXF group 11 represents the text alignment point and the value of DXF group 10 (the insertion point) is ignored if supplied (though, the group must be present).
For example, for middle-center justified single-line text, you might use the following:
(defun mytext ( ins hgt str )
(entmakex
(list
'(000 . "TEXT")
(cons 010 ins)
(cons 011 ins)
(cons 040 hgt)
(cons 001 str)
'(072 . 1)
'(073 . 2)
)
)
)
Here:
DXF group 0 is the entity type
DXF group 10 is the text insertion point (used if both DXF 72 and 73 are zero)
DXF group 11 is the text alignment point (used if either DXF 72 or 73 are non-zero)
DXF group 40 is the text height
DXF group 1 is the text content
DXF group 72 determines the horizontal alignment
DXF group 73 determines the vertical alignment
If you want to create single-line text which adheres to the properties of the active UCS (for example, created in the UCS construction plane, rotated to align with the UCS x-axis), you can use the following:
(defun mytext ( ins hgt str )
(
(lambda ( ocs )
(entmakex
(list
'(000 . "TEXT")
(cons 010 (trans ins 1 ocs))
(cons 050 (angle '(0.0 0.0) (trans (getvar 'ucsxdir) 0 ocs t)))
(cons 040 hgt)
(cons 001 str)
(cons 210 ocs)
)
)
)
(trans '(0.0 0.0 1.0) 1 0 t)
)
)
This assumes that the insertion point will be supplied relative to the active UCS, e.g.:
(defun c:test ( )
(mytext
(progn
(initget 1)
(getpoint "\nSpecify insertion point: ")
)
(progn
(initget 7)
(getdist "\nSpecify text height: ")
)
(getstring t "\nSpecify text content: ")
)
)
A reference for all of the DXF groups applicable to a TEXT entity may be found here.

Scheme add columns in a matrix

I am trying to write a function that takes a matrix (represented as a list of lists) and adds the elements down the columns and returns a vector (represented as a list):
Example:
(define sample
'((2 6 0 4)
(7 5 1 4)
(6 0 2 2)))
should return '(15 11 3 10).
I was trying to use the (list-ref) function twice to obtain the first element of each column with no luck. I am trying something like:
(map (lambda (matrix) ((list-ref (list-ref matrix 0) 0)) (+ matrix))
The solution is simple if we forget about the indexes and think about higher-order procedures, try this:
(define sample
'((2 6 0 4)
(7 5 1 4)
(6 0 2 2)))
(apply map + sample)
=> '(15 11 3 10)
Explanation: map can take multiple lists as arguments. If we apply it to sample (which is a list of lists) and pass + as the procedure to do the mapping, it'll take one element from each list in turn and add them, producing a list with the results - effectively, adding all the columns in the matrix.

ALU-n Procedure in Scheme

I'm a beginner to the Scheme language, so I'm having trouble writing a procedure to take in an n-bit number and put it into an ALU. The ALU is supposed to be constructed using 1-bit ALU's.
Here is the 1-bit ALU:
(define ALU1
(lambda (sel a b carry-in)
(multiplexor4 sel
(cons (andgate a b) 0)
(cons (orgate a b) 0)
(cons (xorgate a b) 0)
(multiplexor2 sub
(full-adder a b carry-in)
(full-adder a (notgate b) carry-in)))))
which, along with the multiplexors and full-adder, works.
Here is my attempt at using a couple of procedures to simulate the n-bit ALU:
(define ALU-helper
(lambda (selection x1 x2 carry-in n)
(if (= n 0)
'()
(ALU1 (selection x1 x2 carry-in)))))
(define ALUn
(lambda (selection x1 x2 n)
(ALU-helper (selection x1 x2 c n))))
And when it's done, it's supposed to take 2 n-bit numbers and add them, or subtract etc, according the to "selection." This would be the input:
(define x1 '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0) )
(define x2 '(1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) )
(ALUn 'add x1 x2 32)
And I get errors when running it that seem to be happening because of the "selection" parameter. I'm sure I'm just getting confused by all the parameters, but I'm not sure how to fix the problem and get the ALU to work. I'm running this using the Dr. Racket program, language R5RS.
By putting parentheses around your arguments to ALU1 inside ALU-helper, you are asking selection to be treated as a function, and only passing 1 argument to ALU-helper. Try:
(ALU1 selection x1 x2 carry-in))))
Same thing for the call to ALU-helper in ALUn.

Resources