text aligning by dxf groups - autocad

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.

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)

iterate through a list of lists of strucs in Racket?

given a list of the following form:
(define lst (list
(list
(make-route-section 32 'ordinary-road 23 0.45)
(make-route-section 54 'ordinary-road 92 0.83)
(make-route-section 14 'street 8 0.82)
(make-route-section 44 'ferry 34 0.64)
(make-route-section 96 'ferry 249 0.41)
)(
list
(make-route-section 92 'ordinary-road 12 0.44)
(make-route-section 98 'highway 45.243 0.3)
(make-route-section 44 'ordinary-road 34.4 0.64)
(make-route-section 39 'street 6 0.8)
)(
list
(make-route-section 62 'highway 82 0.35)
(make-route-section 58 'street 4 0.46)
(make-route-section 50 'highway 81 0.24)
(make-route-section 75 'highway 67.3 0.39)
)
)
A route section is the following:
(define-struct route-section (id kind length eco-index))
(define-struct traffic-jam (section-id delay))
How would I, for example filter out certain structs with given ids (first field of the struct) with a list like (list 32 62) and it would return me the second element of the outer list. What confused me especially is, when I entered (second lst) it returned the second element of the first list of the whole list.
How would I add up the lengths of the route sections? I feel so dumb, even using higher order functions I could't iterate through the nested list.
Here's how to sum the length of the route.
A route is a list of segments, each segment is a list of sections, which are structures.
To sum a list of numbers a good approach is (foldl + 0 <list>). But we don't have a list of numbers, we have a list of segments. So we want to get a number from each segment. We could use foldl again. But each element of the segment isn't a number: it's a section. So a good way of taking a list of somethings and getting a list of something-elses is to use (map <function-to-turn-something-into-something-else> <list-of-somethings>).
route-section-length will take a section object and return its length, so that's the function we want to map. So we could start, on a segment, to turn it into a list of lengths of its sections:
(map route-section-length <segment>)
And now we have a list of numbers for each segment, and we can use foldl:
(foldl + 0 (map route-section-length <segment>))
And this will turn a segment into a number. Well, we have a list of segments, so we want to turn them into a list of numbers, which we can do by mapping a function whose body is the expression above over the list:
(map (λ (segment)
(foldl + 0 (map route-section-length segment)))
route)
OK, that gives us a list of numbers, which we now need to add up, again using foldl:
(foldl + 0
(map (λ (segment)
(foldl + 0 (map route-section-lenght segment)))
route))
And that, wrapped in a definition, is what we want.
Also: indent your code properly.

How to export flattened image with GIMP Script-Fu

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)
)

hb-append vs hbl-append and ht-append vs htl-append in pict

Racket's pict, has several combinators for combining other pictures. These docs contain a nice table for how its *-append combinators work:
Most of these examples make sense. The first letter is v or h for vertical and horizontal respectively. The second letter is l, t, or r for vertical-left/center/right, or t c b, for horizontal-top/center/bottom.
However, this leaves out htl-append and hbl-append. Neither of them fit into this pattern. Additionally, they both seem to match hb-append on the table.
So, what is htl-append and hbl-append used for, and how does it differ from hb-append?
I wrote that example in the docs, so I guess this is sort of my fault and so I'll attempt to answer the question.
The example doesn't illustrate the difference very well because it's using shapes and not text.
Changing the example to use text + shapes shows that there is some differences between these functions:
#lang racket
(require pict)
(inset
(cbl-superimpose
(hb-append 10
(frame (text "g" "Helvetica" 30))
(rectangle 10 10 #:border-width 2))
(hline 200 2))
10)
(blank 1 30)
(inset
(cbl-superimpose
(hbl-append 10
(frame (text "g" "Helvetica" 30))
(rectangle 10 10 #:border-width 2))
(hline 200 2))
10)
(blank 1 30)
(inset
(ctl-superimpose
(ht-append 10
(frame (text "i" "Helvetica" 30))
(rectangle 10 10 #:border-width 2))
(hline 200 2))
10)
(blank 1 30)
(inset
(ctl-superimpose
(htl-append 10
(frame (text "i" "Helvetica" 30))
(rectangle 10 10 #:border-width 2))
(hline 200 2))
10)
If you run this example you'll get 4 pictures that show different cases. Depending on the letters, you get different alignments due to the ascenders/descenders. It would probably be more useful for the docs to show an example similar to this one with text.
If you want to mix pictures and text it often makes sense to use the l- variants to avoid an odd look where the picture sticks out:
#lang racket
(require pict)
(hb-append 10
(text "hug" "Helvetica" 30)
(rectangle 20 20 #:border-width 2)
(text "hug" "Helvetica" 30))
(hbl-append 10
(text "hug" "Helvetica" 30)
(rectangle 20 20 #:border-width 2)
(text "hug" "Helvetica" 30))
The slideshow tutorial uses this picture to explain hbl-append and htl-append :
(With hb-append and ht-append, the frames around each pict would be aligned.)

Parsing strings representing lists of integers and integer spans

I am looking for a function that parses integer lists in Emacs Lisp, along the lines of Perl's Set::IntSpan. I.e., I would like to be able to do something like this:
(parse-integer-list "1-3, 4, 8, 18-21")
⇒ (1 2 3 4 8 18 19 20 21)
Is there an elisp library somewhere for this?
The following does what you want:
(defun parse-integer-list (str)
"Parse string representing a range of integers into a list of integers."
(let (start ranges)
(while (string-match "\\([0-9]+\\)\\(?:-\\([0-9]+\\)\\)?" str start)
(push
(apply 'number-sequence
(seq-map 'string-to-int
(seq-filter
'identity
(list (match-string 1 str) (match-string 2 str)))))
ranges)
(setq start (match-end 0)))
(nreverse (seq-mapcat 'nreverse ranges))))
The code loops over the incoming string searching for plain numbers or ranges of numbers. On each match it calls number-sequence with either just a number for a plain match or two numbers for a range match and pushes each resulting number sequence into a list. To account for push building the result backwards, at the end it reverses all ranges in the list, concatenates them, then reverses the result and returns it.
Calling parse-integer-list with your example input:
(parse-integer-list "1-3, 4, 8, 18-21")
produces:
(1 2 3 4 8 18 19 20 21)

Resources