Getting the measurements for stretching - lisp

I am creating an algorithm to help me expand boxes to the correct size, as such:
I made a code that asks two points that should be the new depth and measures them. Then subtracts to the original depth of the block (0.51) and then asks the side to stretch.
(defun mystretchh ( dis / pt1 pt2 sel )
(while
(and
(setq pt1 (getpoint "\nFirst point of selection window: "))
(setq pt2 (getcorner pt1 "\nSecond point of selection window: "))
(not (setq sel (ssget "_C" pt1 pt2)))
)
(princ "\nNo objects where found in the selection window.")
)
(if sel
(progn
(command "_.stretch" sel "" "_non" '(0 0) "_non" (list dis 0))
t
)
)
)
(defun c:test (/ a x c p)
;ungroup
(command "pickstyle" 0)
;variables
(initget (+ 1 2 4))
(setq p (getpoint "\nSelect first point: "))
(setq c (getpoint "\nSelect second point: "))
(command "_.dist" p c)
(setq x (rtos (getvar 'distance) 2 3))
;calculate distance to stretch
(setq a (- x 0.51))
;stretch
(mystretchh a)
;regroup
(command "pickstyle" 1)
(print "Module modified.")
(princ)
)
I have two problems:
The stretch is working backwards, I tried using negative values to no avail.
It reports a syntax error, but I cannot find it.
I haven't touched AutoLISP for half a year - maybe some of you can find the problems in a blink of an eye.

You can calculate the distance between two points using the standard distance function, rather than calling the DIST command and then retrieving the value of the DISTANCE system variable.
Hence, this:
(command "_.dist" p c)
(setq x (rtos (getvar 'distance) 2 3))
Can become:
(setq x (rtos (distance p c) 2 3))
However, you are receiving a syntax error because you have converted the distance to a string using rtos, and then you are attempting to perform arithmetic on the string here:
(setq a (- x 0.51))
There is no need to convert the distance to a string, and so these expressions can become:
(setq a (- (distance p c) 0.51))
You may also want to check whether (distance p c) is greater than 0.51 before performing this subtraction to avoid unexpected results.
To determine the appropriate direction, since your current code can only stretch along the x-axis, you'll need to check whether or not the x-coordinate of point p is greater than that of point c.

Related

Troubleshooting a LISP routine that inserts a block with attributes at coordinates defined in a text file

So I asked a similar question here about a month ago (Lisp - Extracting info from a list of comma separated values) and managed to put something together that almost meets my needs, but I am hitting a roadblock with a few things. I'll start with the code:
(defun c:poleid ( / fn fp lst l)
;; String to list convertor. This will separate coordinates and values by comma and store them in a variable as a list
(defun LM:str->lst ( str del / len lst pos )
(setq len (1+ (strlen del)))
(while (setq pos (vl-string-search del str))
(setq lst (cons (substr str 1 pos) lst)
str (substr str (+ pos len))
)
)
(reverse (cons str lst))
)
;; Prompt the user to select a .TXT file.
(setq fn (getfiled "Select UTM GPS file" "" "txt" 4))
;; Open the file and create an empty list
(setq fp (open fn "r") lst '())
;; Iterate the file, writing each line to the list (as a string)
(while (setq l (read-line fp))
(setq lst (cons l lst))
)
;; Close the file.
(close fp)
;; Reverse the list
(setq lst (reverse lst))
;; At this point, the data is stored in a variable (lst) and the file is closed.
;; Save current OSNAP MODE and turn off
(setq os (getvar 'osmode))
(setvar "osmode" 0)
;;Set pcount to 0
(setq pcount 0)
;; Iterate the list and draw a point
;; entity at each coordinate
(foreach item lst ;; For each line in lst
(setq items (LM:str->lst item ",")) ;;set variable items as a list of item, separated by commas. Set the las
(setq ptx (nth 2 items) pty (nth 1 items) ptz (nth 3 items) idn (nth 4 items)) ;; Set the pole (pt) x, y and z values from the 2nd, 3rd and 4th values of each line. Set notes to idn (as a string). UTM values are provided to this program as y,x,z
(setq idr (LM:str->lst idn " ") idn (nth 0 idr) idr (nth 1 idr)) ;;Set idr (Pole ID) as a list of idn, then set idn as the first half of the note (HP#) and idr as the second half
(cond ((wcmatch idn "HP") ;; Only process lines that have HP in the 5th value
(
(printc idn)
(setq ptxyz (strcat ptx "," pty "," ptz)) ;;Make the pole x, y, and z value into a single string, separated by commas
(setq idx (atof ptx) idx (- idx 5.0) idx (rtos idx)) ;;set the idx as real number version of ptx, subtract 5 from it, then convert back to a string
(setq idxyz (strcat idx "," pty "," ptz)) ;;Make the ID x, y, and z value into a single string, separated by commas
;;Insert pole and ID block at xyz coords, with idn as the HP number and idr as the pole ID
(command "insert" "G:\\Shared drives\\Project Tools\\Customized Tools\\CAD\\prog\\CWood_Pole_D.dwg" ptxyz "508" "508" "0") ;; Pole symbol set to an x/y scale of 20
(command "insert" "G:\\Shared drives\\Project Tools\\Customized Tools\\CAD\\prog\\POLENA.dwg" idxyz "25.4" "25.4" "0" idn idr) ;; Pole ID block set to an x/y scale of 1, with the top half showing the HP# and the bottom half showing the pole ID
(setq pcount (+ pcount 1)) ;;Add 1 to counter
))
)
)
;; Restore OSNAP MODE and close with count of poles inserted
(setvar 'osmode os)
(setq pcount (write-to-string pcount))
(princ pcount)
(princ " pole(s) have been successively added")
(princ)
)
This is fed a .txt file that contains GPS points. The test example I have been feeding the script is:
1000,1,2,3,HP
1001,10.000,2.000,3.000,HP21 blah
1002,15.000,2.000,3.000,HP22 2gt3
1003,20.000,2.000,3.000,CU
#,Easting,Northing,Elevation,Notes
What I want the code to do is insert a block (CWood_Pole_D.dwg) in at the Easting/Northing/Elevation, and then insert a second block (POLENA.dwg) 5 units to the left of that point. The second block contains two attributes, which I would like to pull from the notes (with the two attributes separated by a space). All of this should only happen when the notes begin with "HP" (which may be followed bynumbers and a letter, ex. HP22A). The last little bit just counts up each time a pair of blocks is successfully added, but even that is spitting out a .
The problem I am having is dealing with the notes part, and conversely the loop activating when the notes are anything but JUST "HP". I'm also sure there is a bunch of redundancy or useless code, but boy oh boy is it difficult to find good information that breaks down all the individual actions (like, what is happening with the Lee Mac string to list convertor?)
I think the match does not work:
(wcmatch idn "HP")
You can try this instead, you may need a wildcard to match e.g. "HP22":
(wcmatch idn "HP*")
The rest is fine, I'd encourage to split your setq into distinct lines for readability.

Why are circle center coordinates (key 10) not relative to the origin in DXF data?

I need this piece of information for a filter that I'm creating.
So let's say I set my grid and snap to 1 for example and then I place the origin with UCS.
And then I draw a circle with center 5, 0.
Here is what I get:
(
(-1 . <Entity name: 1f3dbb9d580>)
(0 . "CIRCLE")
(330 . <Entity name: 1f3dbba51f0>)
(5 . "270")
(100 . "AcDbEntity")
(67 . 0)
(410 . "Model")
(8 . "0")
(100 . "AcDbCircle")
(10 2495.0 1180.0 0.0)
(40 . 3.16228)
(210 0.0 0.0 1.0)
)
Why at number 10 I have those numbers?
Shouldn't it be like (10 5.0 0.0 0.0)?
The coordinates defining the geometry of the majority of planar entities (such as arcs, circles, 2D polylines etc.) are defined relative to a coordinate system known as the Object Coordinate System (OCS).
The OCS shares its origin with the World Coordinate System (WCS), with its Z-axis corresponding to the normal vector (aka extrusion vector) associated with the entity (represented by DXF group 210), and its X & Y axes defined by the Arbitary Axis Algorithm applied to the normal vector.
The Arbitrary Axis Algorithm is implemented in the standard AutoLISP trans function, which facilitates easy transformation of points from one coordinate system to another.
In your particular example, the normal vector is (0.0 0.0 1.0), which is equal to the normal vector of the WCS plane, and so for this particular example, the OCS is equal to the WCS.
However, in general, to translate points from an arbitrary OCS to either the WCS or the active User Coordinate System (UCS), you would supply the trans function with either the OCS normal vector or the entity name of the entity in question.
For example, translating from OCS to the active UCS using the OCS normal vector:
(trans (cdr (assoc 10 <dxf-data>)) (cdr (assoc 210 <dxf-data>)) 1)
Or, translating from OCS to the active UCS using the entity name:
(trans (cdr (assoc 10 <dxf-data>)) (cdr (assoc -1 <dxf-data>)) 1)
Implemented in a sample program, this might be:
(defun c:test ( / ent enx )
(cond
( (not (setq ent (car (entsel "\nSelect circle: "))))
(princ "\nNothing selected.")
)
( (/= "CIRCLE" (cdr (assoc 0 (setq enx (entget ent)))))
(princ "\nThe selected object is not a circle.")
)
( (princ "\nThe circle center relative to the UCS is: ")
(princ (trans (cdr (assoc 10 enx)) ent 1))
)
)
(princ)
)
Addressing the issues you are encountering as described in your comments, you'll need to transform the coordinates from/to the OCS & UCS to achieve the desired result, for example:
(defun c:test ( / ent enx new old xco )
(cond
( (not (setq ent (car (entsel "\nSelect circle: "))))
(princ "\nNothing selected.")
)
( (/= "CIRCLE" (cdr (assoc 0 (setq enx (entget ent)))))
(princ "\nThe selected object is not a circle.")
)
( (setq old (assoc 10 enx)
xco (car (trans (cdr old) ent 1))
new (cons 10 (trans (list xco 0.0 0.0) 1 ent))
enx (subst new old enx)
)
(entmod enx)
)
)
(princ)
)
The operation could also be condensed to a single expression, e.g.:
(setq old (assoc 10 enx)
enx (subst (cons 10 (trans (list (car (trans (cdr old) ent 1)) 0) 1 ent)) old enx)
)
(entmod enx)
However, this is less readable.
In this list coordinates are in WCS. So if You draw circle using command and active UCS is differend than WCS, everything is OK the result is as expected.
To translate coordinates between coordinate systems You can use
(trans (assoc 10 YourList) 0 1 nil)

Common Lisp: "no non-white-space characters in string"

For Project Euler Problem 8, I am told to parse through a 1000 digit number.
This is a brute-force Lisp solution, which basically goes through every 5 consecutive digits and multiplies them from start to finish, and returns the largest one at the end of the loop.
The code:
(defun pep8 ()
(labels ((product-of-5n (n)
(eval (append '(*)
(loop for x from n to (+ n 5)
collect (parse-integer
1000digits-str :start x :end (+ x 1)))))))
(let ((largestproduct 0))
(do ((currentdigit 0 (1+ currentdigit)))
((> currentdigit (- (length 1000digits-str) 6)) (return largestproduct))
(when (> (product-of-5n currentdigit) largestproduct)
(setf largestproduct (product-of-5n currentdigit)))))))
It compiles without any warnings, but upon running it I get:
no non-whitespace characters in string "73167176531330624919225119674426574742355349194934...".
[Condition of type SB-INT:SIMPLE-PARSE-ERROR]
I checked to see if the local function product-of-5n was working by writing it again as a global function:
(defun product-of-5n (n)
(eval (append '(*)
(loop for x from n to (+ n 5)
collect (parse-integer
1000digits-str :start x :end (+ x 1))))))
This compiled without warnings and upon running it, appears to operate perfectly. For example,
CL_USER> (product-of-5n 1) => 882
Which appears to be correct since the first five digits are 7, 3, 1, 6 and 7.
As for 1000digits-str, it was simply compiled with defvar, and with Emacs' longlines-show-hard-newlines, I don't think there are any white-space characters in the string, because that's what SBCL is complaining about, right?
I don't think there are any white-space characters in the string, because that's what SBCL is complaining about, right?
The error-message isn't complaining about the presence of white-space, but about the absence of non-white-space. But it's actually a bit misleading: what the message should say is that there's no non-white-space in the specific substring to be parsed. This is because you ran off the end of the string, so were parsing a zero-length substring.
Also, product-of-5n is not defined quite right. It's just happenstance that (product-of-5n 1) returns the product of the first five digits. Strings are indexed from 0, so (product-of-5n 1) starts with the second character; and the function iterates from n + 0 to n + 5, which is a total of six characters; so (product-of-5n 1) returns 3 × 1 × 6 × 7 × 1 × 7, which happens to be the same as 7 × 3 × 1 × 6 × 7 × 1.
EVAL is not a good idea.
Your loop upper bound is wrong.
Otherwise I tried it with the number string and it works.
It's also Euler 8, not 9.
This is my version:
(defun euler8 (string)
(loop for (a b c d e) on (map 'list #'digit-char-p string)
while e maximize (* a b c d e)))
since I don't know common lisp, I slightly modified your code to fit with elisp. As far as finding bugs go and besides what have been said ((product-of-5n 1) should return 126), the only comment I have is that in (pep8), do length-4 instead of -6 (otherwise you loose last 2 characters). Sorry that I don't know how to fix your parse-error (I used string-to-number instead), but here is the code in case you find it useful:
(defun product-of-5n (n) ;take 5 characters from a string "1000digits-str" starting with nth one and output their product
(let (ox) ;define ox as a local variable
(eval ;evaluate
(append '(*) ;concatenate the multiplication sign to the list of 5 numbers (that are added next)
(dotimes (x 5 ox) ;x goes from 0 to 4 (n is added later to make it go n to n+4), the output is stored in ox
(setq ox (cons ;create a list of 5 numbers and store it in ox
(string-to-number
(substring 1000digits-str (+ x n) (+ (+ x n) 1) ) ;get the (n+x)th character
) ;end convert char to number
ox ) ;end cons
) ;end setq
) ;end dotimes, returns ox outside of do, ox has the list of 5 numbers in it
) ;end append
) ;end eval
) ;end let
)
(defun pep8 () ;print the highest
(let ((currentdigit 0) (largestproduct 0)) ;initialize local variables
(while (< currentdigit (- (length 1000digits-str) 4) ) ;while currentdigit (cd from now on) is less than l(str)-4
;(print (cons "current digit" currentdigit)) ;uncomment to print cd
(when (> (product-of-5n currentdigit) largestproduct) ;when current product is greater than previous largestproduct (lp)
(setq largestproduct (product-of-5n currentdigit)) ;save lp
(print (cons "next good cd" currentdigit)) ;print cd
(print (cons "with corresponding lp" largestproduct)) ;print lp
) ;end when
(setq currentdigit (1+ currentdigit)) ;increment cd
) ;end while
(print (cons "best ever lp" largestproduct) ) ;print best ever lp
) ;end let
)
(setq 1000digits-str "73167176531330624919")
(product-of-5n 1)
(pep9)
which returns (when ran on the first 20 characters)
"73167176531330624919"
126
("next good cd" . 0)
("with corresponding lp" . 882)
("next good cd" . 3)
("with corresponding lp" . 1764)
("best ever lp" . 1764)
I've done this problem some time ago, and there's one thing you are missing in the description of the problem. You need to read consequent as starting at any offset into a sting, not only the offsets divisible by 5. Therefore the solution to the problem will be more like the following:
(defun pe-8 ()
(do ((input (remove #\Newline
"73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450"))
(tries 0 (1+ tries))
(result 0))
((= tries 5) result)
(setq result
(max result
(do ((max 0)
(i 0 (+ 5 i)))
((= i (length input)) max)
(setq max
(do ((j i (1+ j))
(current 1)
int-char)
((= j (+ 5 i)) (max current max))
(setq int-char (- (char-code (aref input j)) 48))
(case int-char
(0 (return max))
(1)
(t (setq current (* current int-char))))))))
input (concatenate 'string (subseq input 1) (subseq input 0 1)))))
It's a tad ugly, but it illustrates the idea.
EDIT sorry, I've confused two of your functions. So that like was incorrect.

Lisp macro that does loop "unrolling"

My first steps with Lisp macros...
(defconstant width 7)
(defconstant height 6)
...
; board is a 2D array of width x height
; and this is my first ever macro:
(defmacro at (y x)
`(aref board ,y ,x))
; "board" must be available wherever the macro is used.
(defun foo (board ...)
...
(loop for y from 0 to (1- height) do
; thanks to the "at" macro, this is cleaner:
(let ((score (+ (at y 0) (at y 1) (at y 2))))
(loop for x from 3 to (1- width) do
(incf score (at y x))
; ...do something with score
(decf score (at y (- x 3)))))))
The code uses my first ever macro, the "at" one. It emits "access instructions" to read from board[y][x], so it can only be used in places where "board" exists, like the function "foo" above.
This worked - and then I realized that... I can go further.
The two nested loops are "statically" constrained: from 0 to height-1 for y, from 3 to (width-1) for x... so in theory, I can create a macro that emits (unrolls!) the exact incf and decf instructions done in the loops' code!
I tried this:
(defmacro unroll ()
(loop for y from 0 to (1- height) do
`(setf score (+ (at ,y 0) (at ,y 1) (at ,y 2)))
(loop for x from 3 to (1- width) do
`(incf score (at ,y ,x))
`(decf score (at ,y (- ,x 3))))))
...but failed - "(macroexpand-1 '(unroll))" shows me NIL.
What am I doing wrong?
In case it is not clear, I want to use two nested loops and emit "code" at the beginning of the outer loop, and for each iteration of the inner loop.
Any help most appreciated (I am a LISP newbie).
UPDATE: After #larsmans' kind advice, I succeeded in applying this change to my code - and to my immense satisfaction, I watched the Lisp version of my Score4 algorithm become the 2nd fastest implementation, behind only C and C++ (and faster than OCaml!).
You should collect the statements you generate inside the macro's loop, not pretend to execute them with do:
(defmacro unroll ()
(loop for y from 0 to (1- height)
collect
`(begin (setf score (+ (at ,y 0) (at ,y 1) (at ,y 2)))
,#(loop for x from 3 to (1- width)
collect `(begin (incf score (at ,y ,x))
(decf score (at ,y (- ,x 3))))))))

Setting Emacs to Split Buffers Side-by-Side

A lot of Emacs functions automatically split the screen. However, they all do so such that the windows are one on top of the other. Is there any way to make them split such that they are side-by-side by default instead?
(setq split-height-threshold nil)
(setq split-width-threshold 0)
GNU Emacs Lisp Reference Manual: Choosing Window Options
Two solutions here, use any one you like:
A: Vertically(left/right) by default:
(setq split-height-threshold nil)
(setq split-width-threshold 0)
B: Automatically split window vertically(left/right) if current window is wide enough
(defun display-new-buffer (buffer force-other-window)
"If BUFFER is visible, select it.
If it's not visible and there's only one window, split the
current window and select BUFFER in the new window. If the
current window (before the split) is more than 100 columns wide,
split horizontally(left/right), else split vertically(up/down).
If the current buffer contains more than one window, select
BUFFER in the least recently used window.
This function returns the window which holds BUFFER.
FORCE-OTHER-WINDOW is ignored."
(or (get-buffer-window buffer)
(if (one-window-p)
(let ((new-win
(if (> (window-width) 100)
(split-window-horizontally)
(split-window-vertically))))
(set-window-buffer new-win buffer)
new-win)
(let ((new-win (get-lru-window)))
(set-window-buffer new-win buffer)
new-win))))
;; use display-buffer-alist instead of display-buffer-function if the following line won't work
(setq display-buffer-function 'display-new-buffer)
Put any one in you .emacs/init.el file.
You can change the "100" to the value you like, depending on you screen.
If you got two windows in one frame, and you want to change the layout from vertical to horizontal or vice verse, here is a solution:
(defun toggle-window-split ()
(interactive)
(if (= (count-windows) 2)
(let* ((this-win-buffer (window-buffer))
(next-win-buffer (window-buffer (next-window)))
(this-win-edges (window-edges (selected-window)))
(next-win-edges (window-edges (next-window)))
(this-win-2nd
(not (and (<= (car this-win-edges)
(car next-win-edges))
(<= (cadr this-win-edges)
(cadr next-win-edges)))))
(splitter
(if (= (car this-win-edges)
(car (window-edges (next-window))))
'split-window-horizontally
'split-window-vertically)))
(delete-other-windows)
(let ((first-win (selected-window)))
(funcall splitter)
(if this-win-2nd (other-window 1))
(set-window-buffer (selected-window) this-win-buffer)
(set-window-buffer (next-window) next-win-buffer)
(select-window first-win)
(if this-win-2nd (other-window 1))))))
;; C-x 4 t 'toggle-window-split
(define-key ctl-x-4-map "t" 'toggle-window-split)
Put it in your .emacs/init.el file, Use C-x 4 t to toggle the layout of your windows.
(setq split-height-threshold 0) (setq split-width-threshold 0)
is what i had to use to get the desired behaviour (no horizontal splitting)
Sometimes we need change between Horizontal and Vertical according current display and our requirement (more lines or more columns).
I recommand the great ToggleWindowSplit, And I bind key to "C-c y"
http://www.emacswiki.org/emacs/ToggleWindowSplit
the simple answer of setting 2 variables to nil and 0 didn't work for me, so I wrote 2 simple functions: one just splits the window into NX vertical buffers and opens files named (for example) file.1 file.2 ... file.NX in each and another one does the same think, except does it in 2D (NY rows by NX columns for opening files f.1 f.2 ... f.[NX*NY]). To install, add this code to .emacs:
(defun grid-files-h (nx wx pfx)
"Using dotimes, split the window into NX side-by-side buffers of width WX and load files starting with prefix PFX and ending in numbers 1 through NX"
(let (ox fn k) ; ox is not used, but fn is used to store the filename, and k to store the index string
(dotimes (x (- nx 1) ox) ; go through buffers, x goes from 0 to nx-2 and ox is not used here
; (print x)
(setq k (number-to-string (+ x 1) ) ) ; k is a string that goes from "1" to "nx-1"
; (print k)
(setq fn (concat pfx k) ) ; fn is filename - concatenate prefix with k
; (print fn)
(find-file fn) ; open the filename in current buffer
(split-window-horizontally wx) ; split window (current buffer gets wx-columns)
(other-window 1) ; switch to the next (right) buffer
)
(setq k (number-to-string nx )) ; last (rightmost) buffer gets the "nx" file
(setq fn (concat pfx k) ) ; fn = "pfx"+"nx"
(find-file fn ) ; open fn
(other-window 1) ; go back to the first buffer
)
)
(defun grid-files-sq (ny wy nx wx pfx)
"Using dotimes, split the window into NX columns of width WX and NY rows of height WY and load files starting with prefix PFX and ending in numbers 1 through NX*NY"
(let (oy ox fn k)
(dotimes (y ny oy) ; go through rows, y goes from 0 to ny-1 and oy is not used here
(split-window-vertically wy) ; create this row
(dotimes (x (- nx 1) ox) ; go through columns, x goes from 0 to nx-2 and ox is not used here
(setq k (number-to-string (+ 1 (+ x (* y nx) ) ) ) ) ; k must convert 2 indecies (x,y) into one linear one (like sub2ind in matlab)
(setq fn (concat pfx k) ) ; filename
(find-file fn ) ; open
(split-window-horizontally wx) ; create this column in this row (this "cell")
(other-window 1) ; go to the next buffer on the right
)
(setq k (number-to-string (+ nx (* y nx) ) ) ) ; rightmost buffer in this row needs a file too
(setq fn (concat pfx k) ) ; filename
(find-file fn ) ; open
(other-window 1) ; go to next row (one buffer down)
)
)
)
and then to use the vertical one, I go to *scratch* (C-x b *scratch* RET,C-x 1), type in (grid-files-h 3 20 "file.") then C-x C-e, or if you want to test out the square qrid one, C-x 1, type in (grid-files-sq 2 15 3 20 "f.") and then C-x C-e and you should see something like
This probably can be done better/more efficiently, but it's a start and it does what I need it to do (display a bunch of sequentially named small files). Feel free to improve or reuse.
I use multiple frames (OSX windows) in emacs regularly for different projects. Here's how I setup a few frames initially split to a left and right window.
(defun make-maximized-split-frame (name)
(let (( f (make-frame (list (cons 'name name))) ))
(maximize-frame f)
(split-window (frame-root-window f) nil t)
))
(make-maximized-split-frame "DocRaptor")
(make-maximized-split-frame "Gauges")
(make-maximized-split-frame "Instrumental")
The direct answer is to press C-c 3.
It's not clear from the question if you want a permanent setting change, but I found this question looking for this answer and didn't find it. (The answer has actually been sitting in a comment for the last 11 years)