pete
/
psc
1
0
Fork 0

Generate docs for funcs defined with defun-q

checker-starter-anchor-sl
Pete Ley 10 months ago
parent 13bc3d869a
commit 5224d442a2

7
.gitignore vendored

@ -1,5 +1,5 @@
*_ls
test.lsp
/test.lsp
stats.html
*.VLX
*.prv
@ -10,4 +10,7 @@ DD_experimental_and_snipets/
_REFERENCE/
checkopen/dist/
checkopen/__pycache__/
checkopen/check.csv
checkopen/check.csv
.projectile
docs/code-manual/build/dist/
docs/code-manual/build/__pycache__/

@ -1,7 +1,7 @@
(defun draw-3d_embed (dat ins / blk em-1 em-2 em-3 em-4 em-5 oc-q oc-x ad-s 45-s)
(setq blk (p-blk (uppercase (xd-value "style" dat))))
(psc-include
'("3d/3d-model.lsp" "shapes/shape.lsp" "shapes/decking.lsp")
'("3D/3d-model.lsp" "shapes/shape.lsp" "shapes/decking.lsp")
) ;_ psc-include
(defun 45-s () ;;; trig for 45 degree stud yz pts
(mapcar '(lambda (l) (* l (cos (dtr 45)))) '(0.5 4.125 4.5))

@ -199,7 +199,7 @@
(setq brk
(line-point-@x
(line-offset
(stair-slope dat)
(stair-slope)
(if (< (read rail_style) 300) 28.545 29.545)
) ;_ line-offset
(cond

@ -484,8 +484,13 @@
(with-data data
(if (not time-debug)
'((d-str) (d-trd) (d-stn) (d-conc) (d-qtr)
(add-3d-conns) (d-3dr))
'((d-str)
(d-trd)
(d-stn)
(d-conc)
(d-qtr)
(add-3d-conns)
(d-3dr))
'((!time '(d-str) nil) (!time '(d-trd) nil)
(!time '(d-stn) nil) (!time '(d-conc) nil)
(!time '(d-qtr) nil) (!time '(add-3d-conns) nil)

@ -1,75 +1,143 @@
(defun set-src-dir ( / users loginname filename)
(setq users '(("pley" . "Q:/home/pete/src/code/")
("Dennis Dreischmeyer" . "C:/dd-dev/")))
(if (setq filename (assoc (getvar "LoginName") users))
(cdr filename)
"S:/psc-src/"))
(setq psc-src-dir (set-src-dir)
psc-data-dir (strcat "C:/Users/" (getvar "LoginName") "/AppData/Local/psc/")
tmpdir (strcat "C:/Users/" (getvar "LoginName") "/AppData/Local/Temp/"))
;; create data dir if necessary
(if (not (vl-file-directory-p psc-data-dir))
(vl-mkdir psc-data-dir))
(defun psc-load (filename / job-dir)
(load (strcat psc-src-dir filename))
(if (and (setq job-dir (find-job-dir))
(member "custom.lsp" (vl-directory-files job-dir)))
;; custom lisp files must be idempotent!
(load (strcat job-dir "/custom.lsp"))))
(load (strcat psc-src-dir "util.lsp"))
(setq psc-included-files '("util.lsp"))
(defun psc-include (filenames)
;; include guards
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; weird documentation load stuff going on here ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; early versions, to be redefined later
;; see util/documentation
(defun defun-r (func-sym)
(setq *delayed-doc* (append *delayed-doc* (list (list *file-name* func-sym)))))
;; functions that are used in defun-r can't have declares but we should add them later
;; DECLARES should be a quoted list without 'declare on the front
(defun declare-late (func-sym declares)
(setq *delayed-decl* (append *delayed-decl* (list (cons func-sym declares)))))
;; see util/test
(defun defun-t (func-sym)
(setq *delayed-test* (append *delayed-test* (list (list *file-name* func-sym)))))
;; defined here so it can be used immediately
;; defun-r called later
(defun-q
set-file-docstring (docstring)
"Registers the current file's docstring
When *doc-build-p* is non-nil, set global *file-docstring* to DOCSTRING and register it in
the global *file-docs*. This instructs %defun-r to register function documentation."
(if *doc-build-p*
(setq *file-docstring* docstring
*file-docs*
(append *file-docs*
(list
(list *file-name*
(substr *file-docstring* 1 (vl-string-position (ascii "\n")
*file-docstring*))
*file-docstring*))))))
((lambda(/ *doc-build-p*)
(setq *doc-build-p* t
*file-name* "acaddoc.lsp"
*file-docs* nil)
(set-file-docstring
"Init file. This file is the entrypoint into the code base")))
(defun-r 'set-file-docstring)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; end weird documentation stuff ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun-q
calc-src-dir ( / devs loginname filename)
"Return the source directory for the current user
If the user is a developer, set *dev-mode* to t."
(setq devs '(("pley" . "Q:/home/pete/src/code/")
("Dennis Dreischmeyer" . "C:/dd-dev/")))
(if (setq filename (assoc (getvar "LoginName") devs))
(progn
(if (not *inhibit-dev-mode*)
(setq *dev-mode* t))
(cdr filename))
"S:/psc-src/"))
(defun-r 'calc-src-dir)
(defun-q
psc-load (*file-name* / job-dir)
"Alias for load so that *FILE-NAME* is set automatically for each file
Assumes the file lives in psc-src-dir."
(load (strcat psc-src-dir *file-name*)))
(defun-r 'psc-load)
(defun-q
psc-include (filenames)
"Include all files in FILENAMES
Load each file and add it to psc-included-files. Ignore files that are already present
there. Assumes the file lives in psc-src-dir."
(foreach filename filenames
(if (not (ends-with filename ".lsp"))
(if (not (wcmatch filename "*.lsp"))
(setq filename (strcat filename ".lsp")))
(if (not (member filename psc-included-files))
(progn
(setq psc-included-files (vl-sort (cons filename psc-included-files) '<))
(psc-load filename)))))
(defun-r 'psc-include)
(defun psc-remove (filenames)
;; include guards
(defun-q
psc-remove (filenames)
"Remove all members of FILENAMES from psc-included-files"
(foreach filename filenames
(if (member filename psc-included-files)
(setq psc-included-files
(vl-remove filename psc-included-files)
) ;_ setq
) ;_ if
) ;_ foreach
) ;_ defun psc-remove
(defun S::STARTUP ()
;; set up lisp extensions
(setq psc-included-files (vl-remove filename psc-included-files)))))
(defun-r 'psc-remove)
(defun-q
psc-autoload (cmds / file)
"Used in %s::startup to register autoloads
CMDS is a list whose car is a filename and cdr is a list of command names.
This function uses %psc-include as its load mechanism. It registers all the commands as
command functions that only load their defining file, which causes them to redefine
themselves after first run."
(setq file (pop! 'cmds))
(mapcar '(lambda (cmd / cmdsym)
(setq cmdsym (read (strcat "C:" cmd)))
(eval (#!
'(defun-q #cmdsym ()
(psc-include (list #(strcat "commands/" file)))
(#cmdsym)))))
cmds))
(defun-r 'psc-autoload)
(defun-q
s::startup ()
"Routine automatically called by AutoCAD after drawing initializes"
(vl-load-com)
(setq acadObj (vlax-get-acad-object)
acadDoc (vla-get-ActiveDocument acadObj)
modelSpace (vla-get-ModelSpace acadDoc))
;; Only load when used.
;; This function is more integrated with our loading system
;; (psc-include, C:RELOADALL) than plain 'autoload'
(defun psc-autoload (cmds / file)
(setq file (pop! 'cmds))
(mapcar '(lambda (cmd / cmdsym)
(setq cmdsym (read (strcat "C:" cmd)))
(eval (#!
'(defun-q #cmdsym ()
(psc-include (list #(strcat "commands/" file)))
(#cmdsym)))))
cmds))
(mapcar 'psc-autoload
modelSpace (vla-get-ModelSpace acadDoc)
psc-src-dir (calc-src-dir)
psc-data-dir (strcat "C:/Users/" (getvar "LoginName") "/AppData/Local/psc/")
tmpdir (strcat "C:/Users/" (getvar "LoginName") "/AppData/Local/Temp/"))
;; create data dir if necessary
(if (not (vl-file-directory-p psc-data-dir))
(vl-mkdir psc-data-dir))
;; util always loaded early
(psc-load "util.lsp")
(setq psc-included-files '("util.lsp"))
(mapcar (if (or *doc-build-p* *test-build-p*)
;; auto-include all files for doc build
'(lambda(f) (psc-include (list (strcat "commands/" (car f)))))
'psc-autoload)
'(
("3d-model.lsp" "3D-MODEL" "3D-RET" "3D-IN-PLACE" "LINE->PIPE")
("bug.lsp" "BUGREPORT" "FEATUREREQ")
("dd-guard.lsp" "DD-GUARD")
("debug.lsp" "DEBUG")
("devmode.lsp" "DEVMODE")
("dimensions.lsp"
"STAIR-DIM-HOR"
"STAIR-DIM-HOR-OVR"
@ -118,4 +186,15 @@
("updatemarkno.lsp" "UPDATEMARKNO")
("update-chk.lsp" "UPDATE-CHK")
("xdatalist.lsp" "XDATALIST")
)))
))
(if *doc-build-p*
(doc-build-html))
(if *test-build-p*
(test-build-lsp))
(if (or *doc-build-p* *test-build-p*)
(progn
(propagate '*doc-build-p* nil)
(propagate '*test-build-p* nil)
(command "close" "y")))
(gc))
(defun-r 's::startup)

@ -0,0 +1,2 @@
pip install colorhash xlwings
python setup.py py2exe

@ -2,42 +2,40 @@ import os
import sys
import xlwings as xl
from colorhash import ColorHash
from typing import Any
colors = {}
# Entry point to this file starts with if __name__ == '__main__'
# line at the bottom
def color_hash(thing):
global colors
if thing not in colors:
colors[thing] = ColorHash(thing, lightness=(0.3, 0.4, 0.5)).rgb
return colors[thing]
# Formatting, alignment, etc
def align_assemblies(sheet: xl.Sheet) -> None:
"Aligns text in the first three columns to the right"
def color_cell(cell):
cell.font.color = color_hash(cell.value)
def color_column(col):
# skip homogeneous columns
if len(set(filter(lambda x: x is not None, col.value))) == 1:
return
for cell in col:
color_cell(cell)
def color_columns():
for col in sheet.range("D2:EZ2").expand("down").columns:
color_column(col)
# (setq range (xl-ExpandRange (xl-GetRange sheet "A1:C1") "down"))
# (foreach row range
# (foreach cell row
# (xl-put-HorizontalAlignment cell acHAlignRight)))
def align_assemblies():
sheet.range("A1:C1").expand(
"down"
).api.HorizontalAlignment = xl.constants.HAlign.xlHAlignRight
def freeze_pane():
def color_assemblies(sheet: xl.Sheet) -> None:
"Sets the background color for the first three columns"
# (setq range (xl-ExpandRange (xl-GetRange sheet "A1:C1") "down"))
# (foreach row range
# (foreach cell row
# (xl-put-Color cell "grey")))
sheet.range("A1:C1").expand("down").color = (240, 240, 240)
def freeze_pane(book: xl.Book) -> None:
"Freeze the first three columns and first row"
a = book.app.api.ActiveWindow
a.FreezePanes = False
a.SplitColumn = 3
@ -45,11 +43,10 @@ def freeze_pane():
a.FreezePanes = True
def autofit():
sheet.range("B1").expand().columns.autofit()
def format_header(sheet: xl.Sheet) -> None:
"""Formats text in the header row
def bold_header():
Formatting: bold, wrap text, heigh 100px, vertical orientation"""
r = sheet.cells.rows[0]
r.font.bold = True
r.wrap_text = True
@ -58,18 +55,79 @@ def bold_header():
r.rows.autofit()
def autofit(sheet: xl.Sheet) -> None:
"Autofits column widths from the second column on"
sheet.range("B1").expand().columns.autofit()
# Coloring columns
# uses ColorHash to assign a different color to each unique value
colors = {} # global colors cache
def color_columns(sheet: xl.Sheet) -> None:
"Sets the font color of all cells to their hashed colors"
for col in sheet.range("D2:EZ2").expand("down").columns:
if col[0].value is None:
return
_color_column(col)
def _color_column(col: xl.Range) -> None:
"""Sets the font color of each cell in col to their hashed colors
If all the cells in col are the same or empty, skip the whole column"""
# skip homogeneous columns
if len(set(filter(lambda x: x is not None, col.value))) == 1:
return
for cell in col:
_color_cell(cell)
def _color_cell(cell: xl.Range) -> None:
"Sets the font color of cell to its hashed color"
cell.font.color = _color_hash(cell.value)
def _color_hash(thing: Any) -> None:
"Returns the cached color of thing, creating if necessary"
global colors
if thing not in colors:
colors[thing] = ColorHash(thing, lightness=(0.3, 0.4, 0.5)).rgb
return colors[thing]
if __name__ == '__main__':
# approximate AutoLISP equivalents for each line in comments
# pretend there is a system that provides an object API similar to
# vla- functions, that uses the prefix xl- (Excel)
# first command line argument (no real AutoLISP equiv)
# sets path variable to the file name path (provided by AutoLISP 'startapp')
path = sys.argv[1]
# (setq book (xl-CreateBook path))
book = xl.Book(path)
# (setq sheet (nth 0 (xl-get-Sheets book)))
sheet = book.sheets[0]
# (xl-Activate (xl-get-App book) t)
book.app.activate(steal_focus=True)
align_assemblies()
freeze_pane()
bold_header()
autofit()
color_columns()
# (align_assemblies sheet), etc
align_assemblies(sheet)
color_assemblies(sheet)
freeze_pane(book)
format_header(sheet)
autofit(sheet)
color_columns(sheet)
# (xl-Save book (string-subst "xls" "csv" path))
book.save(path.replace('csv', 'xls'))
# (vl-file-delete path)
os.remove(path)

@ -0,0 +1,2 @@
(defun c:codemanual ()
(startapp (strcat "cmd.exe /C start " psc-src-dir "docs/code-manual/index.html")))

@ -1,6 +0,0 @@
(psc-include '("util/error.lsp"))
(defun c:debug ()
(princ (strcat "\nDebugging mode " (if debug-flag "OFF" "ON")))
(setq debug-flag (not debug-flag))
(princ))

@ -0,0 +1,4 @@
(defun c:devmode ()
(setq *dev-mode* (not *dev-mode*))
(princ (strcat "\nDevelopment mode " (if *dev-mode* "ON" "OFF")))
(princ))

@ -79,6 +79,6 @@
nil)
(if (and (setq data (dialog-init "fab/hardware.dcl" "hardware" nil))
(setq hdw-row (hdw-table-get-row hdw-table)))
(setq hdw-row (table-get-row hdw-table)))
(hdw-table-set-row hdw-table hdw-row data)))

@ -1,5 +1,5 @@
(psc-include '("commands/embed-dialog.lsp" "3d/3d-embed.lsp"))
(psc-include '("commands/embed-dialog.lsp" "3D/3d-embed.lsp"))
(defun c:embed ( / sel ent dat app rot obj rev)
(setq sel (get-sset '("Embed,3D_Embed")))

@ -47,26 +47,8 @@
( (= (length source) 2)
(cond
( (all-are "Land") (land-multi-edit source))
( (all-are "Stair")
(with-data (read-xdata (car source) "")
'(
(if
(and
(= app_id "3D_Stair")
(vla-block-defined (strcat "P" number "-" level sequence))
) ;_ and
(error
(strcat
"P" number "-" level sequence
" is already defined, cannot create block"
) ;_ strcat
) ;_ error
(land-single-edit source)
) ;_ if
)
) ;_ with-data
) ;_ condif
( (= T) (error-out))
( (all-are "Stair") (land-single-edit source))
( T (error-out))
) ;_ cond
) ;_ condif

@ -325,7 +325,7 @@
(if (vl-catch-all-error-p (setq data (vl-catch-all-apply 'dialog-get-data '(nil))))
(alert "Input errors!\nDefaults not saved")
(if (setq file (open file "w"))
(if (has-duplicates-p data)
(if (has-duplicate-keys-p data)
(progn
(close file)
(vl-file-delete filename)

@ -0,0 +1,116 @@
<!doctype html>
<html>
<head>
<title>acaddoc.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>acaddoc.lsp <a href="../../acaddoc.lsp">[src]</a></h2>
<pre class="fulldoc">Init file. This file is the entrypoint into the code base</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="acaddoc.html#calc-src-dir">(<span class="funcname">calc-src-dir</span>)</a></td>
<td>Return the source directory for the current user</td>
</tr>
<tr>
<td class="funcsig"><a href="acaddoc.html#psc-autoload">(<span class="funcname">psc-autoload</span> cmds)</a></td>
<td>Used in <a href="acaddoc.html#s::startup">s::startup</a> to register autoloads</td>
</tr>
<tr>
<td class="funcsig"><a href="acaddoc.html#psc-include">(<span class="funcname">psc-include</span> filenames)</a></td>
<td>Include all files in FILENAMES</td>
</tr>
<tr>
<td class="funcsig"><a href="acaddoc.html#psc-load">(<span class="funcname">psc-load</span> *file-name*)</a></td>
<td>Alias for load so that *FILE-NAME* is set automatically for each file</td>
</tr>
<tr>
<td class="funcsig"><a href="acaddoc.html#psc-remove">(<span class="funcname">psc-remove</span> filenames)</a></td>
<td>Remove all members of FILENAMES from psc-included-files</td>
</tr>
<tr>
<td class="funcsig"><a href="acaddoc.html#s::startup">(<span class="funcname">s::startup</span>)</a></td>
<td>Routine automatically called by AutoCAD after drawing initializes</td>
</tr>
<tr>
<td class="funcsig"><a href="acaddoc.html#set-file-docstring">(<span class="funcname">set-file-docstring</span> docstring)</a></td>
<td>Registers the current file's docstring</td>
</tr>
</tbody>
</table>
<section>
<h3 id="calc-src-dir" class="funcsig">(<span class="funcname">calc-src-dir</span>)</h3>
<pre class="fulldoc">Return the source directory for the current user
If the user is a developer, set *dev-mode* to t.</pre>
</section>
<section>
<h3 id="psc-autoload" class="funcsig">(<span class="funcname">psc-autoload</span> cmds)</h3>
<pre class="fulldoc">Used in <a href="acaddoc.html#s::startup">s::startup</a> to register autoloads
CMDS is a list whose car is a filename and cdr is a list of command names.
This function uses <a href="acaddoc.html#psc-include">psc-include</a> as its load mechanism. It registers all the commands as
command functions that only load their defining file, which causes them to redefine
themselves after first run.</pre>
</section>
<section>
<h3 id="psc-include" class="funcsig">(<span class="funcname">psc-include</span> filenames)</h3>
<pre class="fulldoc">Include all files in FILENAMES
Load each file and add it to psc-included-files. Ignore files that are already present
there. Assumes the file lives in psc-src-dir.</pre>
</section>
<section>
<h3 id="psc-load" class="funcsig">(<span class="funcname">psc-load</span> *file-name*)</h3>
<pre class="fulldoc">Alias for load so that *FILE-NAME* is set automatically for each file
Assumes the file lives in psc-src-dir.</pre>
</section>
<section>
<h3 id="psc-remove" class="funcsig">(<span class="funcname">psc-remove</span> filenames)</h3>
<pre class="fulldoc">Remove all members of FILENAMES from psc-included-files</pre>
</section>
<section>
<h3 id="s::startup" class="funcsig">(<span class="funcname">s::startup</span>)</h3>
<pre class="fulldoc">Routine automatically called by AutoCAD after drawing initializes</pre>
</section>
<section>
<h3 id="set-file-docstring" class="funcsig">(<span class="funcname">set-file-docstring</span> docstring)</h3>
<pre class="fulldoc">Registers the current file's docstring
When *doc-build-p* is non-nil, set global *file-docstring* to DOCSTRING and register it in
the global *file-docs*. This instructs <a href="util-documentation.html#defun-r">defun-r</a> to register function documentation.</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,2 @@
pip install jinja2
python setup.py py2exe

@ -0,0 +1,124 @@
import json
from jinja2 import Environment, FileSystemLoader
import os
import re
import sys
from typing import Optional, NoReturn
def read_json() -> dict:
"Returns the JSON file files.json as a dictionary"
with open('files.json') as f:
return json.load(f)
def filename_as_html(filename: str) -> str:
"Converts a lsp filename to HTML"
return filename.replace('lsp', 'html').replace('/', '-')
def filelink(value: str, filename: Optional[str] = None) -> str:
"Custom Jinja2 filter for linking to a lsp file"
if filename is None:
filename = value
html_filename = filename_as_html(filename)
return f'<a href="./{html_filename}">{value}</a>'
def funclink(value: str, funcname: Optional[str] = None) -> str:
"Custom Jinja2 filter for linking to a function"
if funcname is None:
funcname = value
filename = filename_as_html(functions[funcname]['file'])
return f'<a href="{filename}#{funcname}">{value}</a>'
def _first_extra_paren(s: str) -> int:
"Returns the index of the first extra right paren in string s"
depth = 0
idx = 0
for c in s:
if c == '(':
depth += 1
if c == ')':
depth -= 1
if depth < 0:
return idx
idx += 1
def prettify_quotes(string: str) -> str:
"Custom Jinja2 filter to turn (QUOTE FORM) into 'FORM"
while (start := string.find('(QUOTE ')) > 0:
s7 = start + 7
end = _first_extra_paren(string[s7:]) + s7
string = string[0:start] + "'" + string[s7:end] + string[end+1:]
return string
def parse_funclinks(string: str) -> str:
"Custom Jinja2 filter for embedding function links"
while match := re.search(r'%[!:&a-zA-Z0-9_<>\*\-]+', string):
match = match[0]
string = re.sub(match, funclink(match[1:]), string, 1)
return string
def write_file_template(env: Environment, file_dict: dict) -> None:
"""Creates a doc page for all the functions in a file dictionary
Uses file.jinja template"""
html_filename = filename_as_html(file_dict['name'])
file_dict['priv_functions'] = []
file_dict['pub_functions'] = []
for func in file_dict['functions']:
file_dict[f"{func['access']}_functions"].append(func)
file_dict['functions'] = file_dict['pub_functions'] + file_dict['priv_functions']
with open(html_filename, 'w') as f:
f.write(env.get_template('file.jinja').render(file_dict))
print(f'Wrote {html_filename}')
def error_pause(e: Exception) -> NoReturn:
"Displays an Exception's message, waits for input, and exits"
print(f"[{type(e).__name__}] {e}")
input("Press ENTER to continue...")
sys.exit()
if __name__ == '__main__':
global files
global functions
try:
os.chdir(sys.argv[1])
files = read_json()
except Exception as e:
error_pause(e)
env = Environment(
loader=FileSystemLoader('templates'),
)
env.filters['filelink'] = filelink
env.filters['funclink'] = funclink
env.filters['parsefunclinks'] = parse_funclinks
env.filters['prettifyquotes'] = prettify_quotes
functions = {func['name']: func for f in files for func in f['functions']}
try:
# write main index file using index.jinja template
with open('index.html', 'w') as f:
f.write(env.get_template('index.jinja').render(
{'files': files,
'functions': sorted(
list(functions.values()),
key=lambda d: d['name'],
)}
))
print("Wrote index.html")
for f in files:
write_file_template(env, f)
except Exception as e:
raise e
error_pause(e)

@ -0,0 +1,4 @@
from distutils.core import setup
import py2exe
setup(console=['build.py'])

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

@ -0,0 +1,146 @@
<!doctype html>
<html>
<head>
<title>stair/util.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>stair/util.lsp <a href="../../stair/util.lsp">[src]</a></h2>
<pre class="fulldoc">Utility functions for stairs.</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr><td colspan="3">Public</td></tr>
<tr>
<td class="funcsig"><a href="stair-util.html#stair-dir">(<span class="funcname">stair-dir</span> ascend z-position)</a></td>
<td>Return the stair direction (either +1 or -1) based on ASCEND and Z-POSITION.</td>
</tr>
<tr>
<td class="funcsig"><a href="stair-util.html#stair-guard-assembly">(<span class="funcname">stair-guard-assembly</span> location)</a></td>
<td>Returns the assembly name of the guard rail at LOCATION.</td>
</tr>
<tr>
<td class="funcsig"><a href="stair-util.html#stair-hr-assembly">(<span class="funcname">stair-hr-assembly</span>)</a></td>
<td>Returns the assembly name of the HG return for this stair.</td>
</tr>
<tr>
<td class="funcsig"><a href="stair-util.html#stair-rail-assembly">(<span class="funcname">stair-rail-assembly</span> side)</a></td>
<td>Returns the assembly name of the stair rail on SIDE of this stair.</td>
</tr>
<tr>
<td class="funcsig"><a href="stair-util.html#stair-slope">(<span class="funcname">stair-slope</span>)</a></td>
<td>Returns the nosing line in slope-intercept form.</td>
</tr>
<tr><td colspan="3">Private</td></tr>
<tr>
<td class="funcsig"><a href="stair-util.html#stair-guard--num">(<span class="funcname">stair-guard--num</span> loc)</a></td>
<td>Returns the number of a guard rail</td>
</tr>
</tbody>
</table>
<section>
<h3 id="stair-dir" class="funcsig">(<span class="funcname">stair-dir</span> ascend z-position)</h3>
<pre class="fulldoc">Return the stair direction (either +1 or -1) based on ASCEND and Z-POSITION.
VARS:
(ASCEND STR (MEMBER ASCEND '("Left" "Right")))
(Z-POSITION STR (MEMBER Z-POSITION '("Near" "Far")))
TESTS:
(= (STAIR-DIR "Left" "Near") 1)
(= (STAIR-DIR "Right" "Far") 1)
(= (STAIR-DIR "Left" "Far") -1)
(= (STAIR-DIR "Right" "Near") -1)</pre>
</section>
<section>
<h3 id="stair-guard-assembly" class="funcsig">(<span class="funcname">stair-guard-assembly</span> location)</h3>
<pre class="fulldoc">Returns the assembly name of the guard rail at LOCATION.
LOCATION should be one of the following strings:
- "ibot" (inside bottom)
- "obot" (outside bottom)
- "itop" (inside top)
- "otop" (outside top)
- "btwn" (between stairs)
WITH-DATA
VARS:
(LOCATION STR (MEMBER LOCATION '("ibot" "obot" "itop" "otop" "btwn")))
(NUMBER STR)
(LEVEL STR)
(SEQUENCE STR)</pre>
</section>
<section>
<h3 id="stair-hr-assembly" class="funcsig">(<span class="funcname">stair-hr-assembly</span>)</h3>
<pre class="fulldoc">Returns the assembly name of the HG return for this stair.
WITH-DATA
VARS:
(ASSEMBLY STR (WCMATCH ASSEMBLY "*S*"))</pre>
</section>
<section>
<h3 id="stair-rail-assembly" class="funcsig">(<span class="funcname">stair-rail-assembly</span> side)</h3>
<pre class="fulldoc">Returns the assembly name of the stair rail on SIDE of this stair.
WITH-DATA
VARS:
(SIDE STR (MEMBER SIDE '("i" "o" "l" "r")))
(NUMBER STR)
(LEVEL STR)</pre>
</section>
<section>
<h3 id="stair-slope" class="funcsig">(<span class="funcname">stair-slope</span>)</h3>
<pre class="fulldoc">Returns the nosing line in slope-intercept form.
WITH-DATA</pre>
</section>
<section>
<h3 id="stair-guard--num" class="funcsig">(<span class="funcname">stair-guard--num</span> loc)</h3>
<pre class="fulldoc">Returns the number of a guard rail
This function calculates the -N at the end of a stair guard rail's sequence number based
on location LOC. See <a href="stair-util.html#stair-guard-assembly">stair-guard-assembly</a> for format of LOC.
If there is no guard rail at LOC, returns nil. If there is only one guard rail on the
flight, returns 0, indicating numbers are unnecessary.
WITH-DATA
VARS:
(LOC STR (MEMBER LOCATION '("ibot" "obot" "itop" "otop" "btwn")))</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1 @@
@import url("../user-manual/style.css")

@ -0,0 +1,13 @@
<!doctype html>
<html>
<head>
<title>{% block title %}{% endblock %} | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
{% block content %}{% endblock %}
</div>
</body>
</html>

@ -0,0 +1,41 @@
{% extends 'base.jinja' %}
{% block title %}{{ name }}{% endblock %}
{% block content %}
<h2>{{ name }} <a href="../../{{ name }}">[src]</a></h2>
<pre class="fulldoc">{{ doc_full|parsefunclinks|prettifyquotes }}</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
{% if priv_functions %}
<tr><td colspan="3">Public</td></tr>
{% endif %}
{% for func in pub_functions %}
<tr>
<td class="funcsig">{{ func['sig']|funclink(func['name']) }}</td>
<td>{{ func['doc_short']|parsefunclinks|prettifyquotes }}</td>
</tr>
{% endfor %}
{% if priv_functions %}
<tr><td colspan="3">Private</td></tr>
{% for func in priv_functions %}
<tr>
<td class="funcsig">{{ func['sig']|funclink(func['name']) }}</td>
<td>{{ func['doc_short']|parsefunclinks|prettifyquotes }}</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
{% for func in functions %}
<section>
<h3 id="{{ func['name'] }}" class="funcsig">{{ func['sig'] }}</h3>
<pre class="fulldoc">{{ func['doc_full']|parsefunclinks|prettifyquotes }}</pre>
</section>
{% endfor %}
{% endblock content %}

@ -0,0 +1,37 @@
{% extends 'base.jinja' %}
{% block title %}Index{% endblock %}
{% block content %}
<h2>Index</h2>
<table>
<thead>
<h3>Files</h3>
<th>Filename</th>
<th>Description</th>
</thead>
<tbody>
{% for file in files %}
<tr>
<td>{{ file['name']|filelink }}</td>
<td>{{ file['doc_short'] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<table>
<thead>
<h3>Functions A-Z ({{ functions|length }} documented)</h3>
<th>Function Signature</th>
<th>Description</th>
</thead>
<tbody>
{% for func in functions %}
<tr>
<td class="funcsig">{{ func['sig']|funclink(func['name']) }}</td>
<td>{{ func['doc_short']|parsefunclinks|prettifyquotes }}</td>
</tr>
{% endfor %}
</tbody>
{% endblock %}

@ -0,0 +1,156 @@
<!doctype html>
<html>
<head>
<title>util/alias.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/alias.lsp <a href="../../util/alias.lsp">[src]</a></h2>
<pre class="fulldoc">Aliases and wrappers</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-alias.html#vla-boolean-r">(<span class="funcname">vla-boolean-r</span> ob1 bop ob2)</a></td>
<td>Wrapper for vla-boolean that returns OB1</td>
</tr>
<tr>
<td class="funcsig"><a href="util-alias.html#vla-logic">(<span class="funcname">vla-logic</span> e)</a></td>
<td>Returns :vlax-true or :vlax-false based on truthiness of E</td>
</tr>
<tr>
<td class="funcsig"><a href="util-alias.html#vla-put-color-r">(<span class="funcname">vla-put-color-r</span> obj col)</a></td>
<td>Wrapper for vla-put-color that returns OBJ</td>
</tr>
<tr>
<td class="funcsig"><a href="util-alias.html#vla-put-layer-r">(<span class="funcname">vla-put-layer-r</span> obj lay)</a></td>
<td>Wrapper for vla-put-layer that returns OBJ</td>
</tr>
<tr>
<td class="funcsig"><a href="util-alias.html#vla-put-normal-r">(<span class="funcname">vla-put-normal-r</span> obj nor)</a></td>
<td>Wrapper for vla-put-normal that returns OBJ</td>
</tr>
<tr>
<td class="funcsig"><a href="util-alias.html#vla-rotate-r">(<span class="funcname">vla-rotate-r</span> obj ins rot)</a></td>
<td>Wrapper for vla-rotate that returns OBJ</td>
</tr>
<tr>
<td class="funcsig"><a href="util-alias.html#vla-rotate3d-r">(<span class="funcname">vla-rotate3d-r</span> obj ax1 ax2 rot)</a></td>
<td>Wrapper for vla-rotate3d that returns OBJ</td>
</tr>
<tr>
<td class="funcsig"><a href="util-alias.html#vla-transformby-r">(<span class="funcname">vla-transformby-r</span> obj mat)</a></td>
<td>Wrapper for vla-transformby that returns OBJ</td>
</tr>
<tr>
<td class="funcsig"><a href="util-alias.html#vlax-put-property-r">(<span class="funcname">vlax-put-property-r</span> obj pro val)</a></td>
<td>Wrapper for vlax-put-property that returns OBJ</td>
</tr>
</tbody>
</table>
<section>
<h3 id="vla-boolean-r" class="funcsig">(<span class="funcname">vla-boolean-r</span> ob1 bop ob2)</h3>
<pre class="fulldoc">Wrapper for vla-boolean that returns OB1
VARS:
(OB1 VLA-OBJECT)
(BOP nil (MEMBER BOP (LIST acUnion acIntersection acSubtraction)))
(OB2 VLA-OBJECT)</pre>
</section>
<section>
<h3 id="vla-logic" class="funcsig">(<span class="funcname">vla-logic</span> e)</h3>
<pre class="fulldoc">Returns :vlax-true or :vlax-false based on truthiness of E</pre>
</section>
<section>
<h3 id="vla-put-color-r" class="funcsig">(<span class="funcname">vla-put-color-r</span> obj col)</h3>
<pre class="fulldoc">Wrapper for vla-put-color that returns OBJ
VARS:
(OBJ VLA-OBJECT)
(COL INT (>= COL 0) (<= COL 256))</pre>
</section>
<section>
<h3 id="vla-put-layer-r" class="funcsig">(<span class="funcname">vla-put-layer-r</span> obj lay)</h3>
<pre class="fulldoc">Wrapper for vla-put-layer that returns OBJ
VARS:
(OBJ VLA-OBJECT)
(LAY STR)</pre>
</section>
<section>
<h3 id="vla-put-normal-r" class="funcsig">(<span class="funcname">vla-put-normal-r</span> obj nor)</h3>
<pre class="fulldoc">Wrapper for vla-put-normal that returns OBJ
VARS:
(OBJ VLA-OBJECT)
(NOR variant)</pre>
</section>
<section>
<h3 id="vla-rotate-r" class="funcsig">(<span class="funcname">vla-rotate-r</span> obj ins rot)</h3>
<pre class="fulldoc">Wrapper for vla-rotate that returns OBJ
VARS:
(OBJ VLA-OBJECT)
(INS variant)
(ROT nil (NUMBERP ROT))</pre>
</section>
<section>
<h3 id="vla-rotate3d-r" class="funcsig">(<span class="funcname">vla-rotate3d-r</span> obj ax1 ax2 rot)</h3>
<pre class="fulldoc">Wrapper for vla-rotate3d that returns OBJ
VARS:
(OBJ VLA-OBJECT)
(AX1 variant)
(AX2 variant)
(ROT nil (NUMBERP ROT))</pre>
</section>
<section>
<h3 id="vla-transformby-r" class="funcsig">(<span class="funcname">vla-transformby-r</span> obj mat)</h3>
<pre class="fulldoc">Wrapper for vla-transformby that returns OBJ
VARS:
(OBJ VLA-OBJECT)
(MAT variant)</pre>
</section>
<section>
<h3 id="vlax-put-property-r" class="funcsig">(<span class="funcname">vlax-put-property-r</span> obj pro val)</h3>
<pre class="fulldoc">Wrapper for vlax-put-property that returns OBJ
VARS:
(OBJ VLA-OBJECT)
(PRO (STR SYM))</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,199 @@
<!doctype html>
<html>
<head>
<title>util/arithmetic.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/arithmetic.lsp <a href="../../util/arithmetic.lsp">[src]</a></h2>
<pre class="fulldoc">Functions related to basic math</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-arithmetic.html#acos">(<span class="funcname">acos</span> ang)</a></td>
<td>Returns the arccosine of ANG (radians)</td>
</tr>
<tr>
<td class="funcsig"><a href="util-arithmetic.html#ceil">(<span class="funcname">ceil</span> n)</a></td>
<td>Returns the smallest integer greater than or equal to N</td>
</tr>
<tr>
<td class="funcsig"><a href="util-arithmetic.html#dec!">(<span class="funcname">dec!</span> int-sym)</a></td>
<td>Decrements INT-SYM destructively</td>
</tr>
<tr>
<td class="funcsig"><a href="util-arithmetic.html#inc!">(<span class="funcname">inc!</span> int-sym)</a></td>
<td>Increments INT-SYM destructively</td>
</tr>
<tr>
<td class="funcsig"><a href="util-arithmetic.html#is-even">(<span class="funcname">is-even</span> n)</a></td>
<td>Returns nil if N is not divisible by 2</td>
</tr>
<tr>
<td class="funcsig"><a href="util-arithmetic.html#post-dec!">(<span class="funcname">post-dec!</span> int-sym)</a></td>
<td>Returns the value of INT-SYM then decrement it destructively</td>
</tr>
<tr>
<td class="funcsig"><a href="util-arithmetic.html#post-inc!">(<span class="funcname">post-inc!</span> int-sym)</a></td>
<td>Returns the value of INT-SYM then increment it destructively</td>
</tr>
<tr>
<td class="funcsig"><a href="util-arithmetic.html#rnd-dn">(<span class="funcname">rnd-dn</span> num prc)</a></td>
<td>Rounds NUM down to the next multiple of precision PRC</td>
</tr>
<tr>
<td class="funcsig"><a href="util-arithmetic.html#rnd-up">(<span class="funcname">rnd-up</span> num prc)</a></td>
<td>Rounds NUM up to the next multiple of precision PRC</td>
</tr>
<tr>
<td class="funcsig"><a href="util-arithmetic.html#square">(<span class="funcname">square</span> n)</a></td>
<td>Returns the square of N</td>
</tr>
<tr>
<td class="funcsig"><a href="util-arithmetic.html#tan">(<span class="funcname">tan</span> ang)</a></td>
<td>Returns the tangent of ANG (radians)</td>
</tr>
</tbody>
</table>
<section>
<h3 id="acos" class="funcsig">(<span class="funcname">acos</span> ang)</h3>
<pre class="fulldoc">Returns the arccosine of ANG (radians)
VARS:
(ANG nil (NUMBERP ANG))</pre>
</section>
<section>
<h3 id="ceil" class="funcsig">(<span class="funcname">ceil</span> n)</h3>
<pre class="fulldoc">Returns the smallest integer greater than or equal to N
VARS:
(N nil (NUMBERP N))
TESTS:
(= (CEIL 0.1) 1)
(= (CEIL 1) 1)
(= (CEIL -0.9) 0)</pre>
</section>
<section>
<h3 id="dec!" class="funcsig">(<span class="funcname">dec!</span> int-sym)</h3>
<pre class="fulldoc">Decrements INT-SYM destructively
VARS:
(INT-SYM SYM (NUMBERP (VL-SYMBOL-VALUE INT-SYM)))</pre>
</section>
<section>
<h3 id="inc!" class="funcsig">(<span class="funcname">inc!</span> int-sym)</h3>
<pre class="fulldoc">Increments INT-SYM destructively
VARS:
(INT-SYM SYM (NUMBERP (VL-SYMBOL-VALUE INT-SYM)))</pre>
</section>
<section>
<h3 id="is-even" class="funcsig">(<span class="funcname">is-even</span> n)</h3>
<pre class="fulldoc">Returns nil if N is not divisible by 2
VARS:
(N INT)
TESTS:
(NOT (IS-EVEN 1))
(IS-EVEN 2)</pre>
</section>
<section>
<h3 id="post-dec!" class="funcsig">(<span class="funcname">post-dec!</span> int-sym)</h3>
<pre class="fulldoc">Returns the value of INT-SYM then decrement it destructively
VARS:
(INT-SYM SYM (NUMBERP (VL-SYMBOL-VALUE INT-SYM)))</pre>
</section>
<section>
<h3 id="post-inc!" class="funcsig">(<span class="funcname">post-inc!</span> int-sym)</h3>
<pre class="fulldoc">Returns the value of INT-SYM then increment it destructively
VARS:
(INT-SYM SYM (NUMBERP (VL-SYMBOL-VALUE INT-SYM)))</pre>
</section>
<section>
<h3 id="rnd-dn" class="funcsig">(<span class="funcname">rnd-dn</span> num prc)</h3>
<pre class="fulldoc">Rounds NUM down to the next multiple of precision PRC
VARS:
(NUM nil (NUMBERP NUM))
(PRC nil (NUMBERP PRC))
TESTS:
(= (RND-DN 1.3 0.25) 1.25)
(= (RND-DN 1.9 0.5) 1.5)</pre>
</section>
<section>
<h3 id="rnd-up" class="funcsig">(<span class="funcname">rnd-up</span> num prc)</h3>
<pre class="fulldoc">Rounds NUM up to the next multiple of precision PRC
VARS:
(NUM nil (NUMBERP NUM))
(PRC nil (NUMBERP PRC))
TESTS:
(= (RND-UP 1.1 0.25) 1.25)
(= (RND-UP 1.1 0.5) 1.5)</pre>
</section>
<section>
<h3 id="square" class="funcsig">(<span class="funcname">square</span> n)</h3>
<pre class="fulldoc">Returns the square of N
VARS:
(N nil (NUMBERP N))
TESTS:
(= (SQUARE 2) 4)
(= (SQUARE -3) 9)</pre>
</section>
<section>
<h3 id="tan" class="funcsig">(<span class="funcname">tan</span> ang)</h3>
<pre class="fulldoc">Returns the tangent of ANG (radians)
VARS:
(ANG nil (NUMBERP ANG))
TESTS:
(= (TAN 0) 0.0)</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,159 @@
<!doctype html>
<html>
<head>
<title>util/comparison.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/comparison.lsp <a href="../../util/comparison.lsp">[src]</a></h2>
<pre class="fulldoc">Functions related to comparisons</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-comparison.html#<f">(<span class="funcname">&lt;f</span> a b fuzz)</a></td>
<td>Returns nil unless A is less than B by FUZZ</td>
</tr>
<tr>
<td class="funcsig"><a href="util-comparison.html#=f">(<span class="funcname">=f</span> a b fuzz)</a></td>
<td>Return nil unless A and B are equal within FUZZ</td>
</tr>
<tr>
<td class="funcsig"><a href="util-comparison.html#>f">(<span class="funcname">&gt;f</span> a b fuzz)</a></td>
<td>Returns nil unless A is greater than B by FUZZ</td>
</tr>
<tr>
<td class="funcsig"><a href="util-comparison.html#identity">(<span class="funcname">identity</span> x)</a></td>
<td>Returns X.</td>
</tr>
<tr>
<td class="funcsig"><a href="util-comparison.html#or*">(<span class="funcname">or*</span> branches)</a></td>
<td>Returns the first non-nil element of BRANCHES.</td>
</tr>
<tr>
<td class="funcsig"><a href="util-comparison.html#sort-by">(<span class="funcname">sort-by</span> func cmp)</a></td>
<td>Returns a lambda for processed sorting</td>
</tr>
<tr>
<td class="funcsig"><a href="util-comparison.html#sort-by-key">(<span class="funcname">sort-by-key</span> key cmp)</a></td>
<td>Returns a lambda for sorting a data list by the value of KEY</td>
</tr>
<tr>
<td class="funcsig"><a href="util-comparison.html#sort-stairland">(<span class="funcname">sort-stairland</span> a b)</a></td>
<td>Sorts a part of stair/landing enames in ascending order.</td>
</tr>
<tr>
<td class="funcsig"><a href="util-comparison.html#sort-strings-asc">(<span class="funcname">sort-strings-asc</span> a b)</a></td>
<td>A comparison function that accounts for length when sorting strings</td>
</tr>
</tbody>
</table>
<section>
<h3 id="<f" class="funcsig">(<span class="funcname">&lt;f</span> a b fuzz)</h3>
<pre class="fulldoc">Returns nil unless A is less than B by FUZZ</pre>
</section>
<section>
<h3 id="=f" class="funcsig">(<span class="funcname">=f</span> a b fuzz)</h3>
<pre class="fulldoc">Return nil unless A and B are equal within FUZZ</pre>
</section>
<section>
<h3 id=">f" class="funcsig">(<span class="funcname">&gt;f</span> a b fuzz)</h3>
<pre class="fulldoc">Returns nil unless A is greater than B by FUZZ</pre>
</section>
<section>
<h3 id="identity" class="funcsig">(<span class="funcname">identity</span> x)</h3>
<pre class="fulldoc">Returns X.
TESTS:
(= (IDENTITY 'X) 'X)</pre>
</section>
<section>
<h3 id="or*" class="funcsig">(<span class="funcname">or*</span> branches)</h3>
<pre class="fulldoc">Returns the first non-nil element of BRANCHES.
or* doesn't eval the branches, so doesn't do short-circuiting.
VARS:
(BRANCHES nil (LISTP BRANCHES))
TESTS:
(= (OR* '(nil 1 2)) 1)
(= (OR* '(nil nil)) nil)</pre>
</section>
<section>
<h3 id="sort-by" class="funcsig">(<span class="funcname">sort-by</span> func cmp)</h3>
<pre class="fulldoc">Returns a lambda for processed sorting
The result of this function is a lambda suitable for passing to vl-sort. CMP should be a
comparison function of 2 arguments that would normally be passed to vl-sort. FUNC should
be a function of 1 argument that will be used to process both arguments to CMP.
Returns the equivalent of '(lambda (a b) (CMP (FUNC a) (FUNC b))).
VARS:
(FUNC (SYM LIST SUBR USUBR))
(CMP (SYM LIST SUBR USUBR))
TESTS:
(EQUAL (SORT-BY 'CAR '<) '(LAMBDA (A B) (< (CAR A) (CAR B))))</pre>
</section>
<section>
<h3 id="sort-by-key" class="funcsig">(<span class="funcname">sort-by-key</span> key cmp)</h3>
<pre class="fulldoc">Returns a lambda for sorting a data list by the value of KEY
The result of this function is a lambda suitable for passing to vl-sort. CMP should be a
comparison function of 2 arguments that would normally be passed to vl-sort. KEY should be
a key from the data list as a string.
VARS:
(KEY STR)
(CMP (SYM LIST SUBR USUBR))</pre>
</section>
<section>
<h3 id="sort-stairland" class="funcsig">(<span class="funcname">sort-stairland</span> a b)</h3>
<pre class="fulldoc">Sorts a part of stair/landing enames in ascending order.
This function is designed to be passed to vl-sort. The resulting sort order will sort by
elevation and put landings in front of stairs at the same level. Multiple landings at the
same level are not considered.</pre>
</section>
<section>
<h3 id="sort-strings-asc" class="funcsig">(<span class="funcname">sort-strings-asc</span> a b)</h3>
<pre class="fulldoc">A comparison function that accounts for length when sorting strings
Pass as comparison argument to vl-sort.</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,217 @@
<!doctype html>
<html>
<head>
<title>util/conversion.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/conversion.lsp <a href="../../util/conversion.lsp">[src]</a></h2>
<pre class="fulldoc">Functions related to conversions</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-conversion.html#?">(<span class="funcname">?</span> x)</a></td>
<td>Alias for (if X X)</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#??">(<span class="funcname">??</span> x default)</a></td>
<td>Alias for (if X X DEFAULT)</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#?f">(<span class="funcname">?f</span> n)</a></td>
<td>Alias for (if N N 0.0)</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#?n">(<span class="funcname">?n</span> n)</a></td>
<td>Alias for (if N N 0)</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#?s">(<span class="funcname">?s</span> s)</a></td>
<td>Alias for (if S S "")</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#collection->list">(<span class="funcname">collection-&gt;list</span> collection)</a></td>
<td>Returns vlax COLLECTION as a list</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#dtr">(<span class="funcname">dtr</span> rad)</a></td>
<td>Returns RAD in radians</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#frac">(<span class="funcname">frac</span> n)</a></td>
<td>Alias for (rtos n 5 2)</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#num-val">(<span class="funcname">num-val</span> x)</a></td>
<td>Alias for (if (numberp X) X 0)</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#rtd">(<span class="funcname">rtd</span> deg)</a></td>
<td>Return DEG in degrees</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#safe-atoi">(<span class="funcname">safe-atoi</span> str)</a></td>
<td>Wrapper for atoi that returns nil if there was no number</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#safearray->list">(<span class="funcname">safearray-&gt;list</span> safearray)</a></td>
<td>Converts arbitrary-dimension SAFEARRAY to a list</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#to-string">(<span class="funcname">to-string</span> x)</a></td>
<td>Returns X as a string</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#unit-dir">(<span class="funcname">unit-dir</span> n)</a></td>
<td>Returns 0 if N is 0, 1 if N is positive, or -1 if N is negative</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#vlax-collection->lst">(<span class="funcname">vlax-collection-&gt;lst</span> collection)</a></td>
<td>Convert COLLECTION to a list of names</td>
</tr>
<tr>
<td class="funcsig"><a href="util-conversion.html#vlist->safearray">(<span class="funcname">vlist-&gt;safearray</span> vertices)</a></td>
<td>Returns VERTICES as a safearray</td>
</tr>
</tbody>
</table>
<section>
<h3 id="?" class="funcsig">(<span class="funcname">?</span> x)</h3>
<pre class="fulldoc">Alias for (if X X)</pre>
</section>
<section>
<h3 id="??" class="funcsig">(<span class="funcname">??</span> x default)</h3>
<pre class="fulldoc">Alias for (if X X DEFAULT)</pre>
</section>
<section>
<h3 id="?f" class="funcsig">(<span class="funcname">?f</span> n)</h3>
<pre class="fulldoc">Alias for (if N N 0.0)</pre>
</section>
<section>
<h3 id="?n" class="funcsig">(<span class="funcname">?n</span> n)</h3>
<pre class="fulldoc">Alias for (if N N 0)</pre>
</section>
<section>
<h3 id="?s" class="funcsig">(<span class="funcname">?s</span> s)</h3>
<pre class="fulldoc">Alias for (if S S "")</pre>
</section>
<section>
<h3 id="collection->list" class="funcsig">(<span class="funcname">collection-&gt;list</span> collection)</h3>
<pre class="fulldoc">Returns vlax COLLECTION as a list
VARS:
(COLLECTION VLA-OBJECT)</pre>
</section>
<section>
<h3 id="dtr" class="funcsig">(<span class="funcname">dtr</span> rad)</h3>
<pre class="fulldoc">Returns RAD in radians
VARS:
(RAD (INT REAL))</pre>
</section>
<section>
<h3 id="frac" class="funcsig">(<span class="funcname">frac</span> n)</h3>
<pre class="fulldoc">Alias for (rtos n 5 2)</pre>
</section>
<section>
<h3 id="num-val" class="funcsig">(<span class="funcname">num-val</span> x)</h3>
<pre class="fulldoc">Alias for (if (numberp X) X 0)</pre>
</section>
<section>
<h3 id="rtd" class="funcsig">(<span class="funcname">rtd</span> deg)</h3>
<pre class="fulldoc">Return DEG in degrees</pre>
</section>
<section>
<h3 id="safe-atoi" class="funcsig">(<span class="funcname">safe-atoi</span> str)</h3>
<pre class="fulldoc">Wrapper for atoi that returns nil if there was no number
(atoi "") => 0
(safe-atoi "") => nil</pre>
</section>
<section>
<h3 id="safearray->list" class="funcsig">(<span class="funcname">safearray-&gt;list</span> safearray)</h3>
<pre class="fulldoc">Converts arbitrary-dimension SAFEARRAY to a list
VARS:
(safearray safearray)</pre>
</section>
<section>
<h3 id="to-string" class="funcsig">(<span class="funcname">to-string</span> x)</h3>
<pre class="fulldoc">Returns X as a string
Essentially a wrapper for vl-prin1-to-string that returns the empty string for nil rather
than "nil".</pre>
</section>
<section>
<h3 id="unit-dir" class="funcsig">(<span class="funcname">unit-dir</span> n)</h3>
<pre class="fulldoc">Returns 0 if N is 0, 1 if N is positive, or -1 if N is negative
Uses a precision fuzz of 0.00001 for comparison. See <a href="util-comparison.html#>f">>f</a>.</pre>
</section>
<section>
<h3 id="vlax-collection->lst" class="funcsig">(<span class="funcname">vlax-collection-&gt;lst</span> collection)</h3>
<pre class="fulldoc">Convert COLLECTION to a list of names
COLLECTION should be a vlax collection of objects that support a Name property.
VARS:
(COLLECTION VLA-OBJECT)</pre>
</section>
<section>
<h3 id="vlist->safearray" class="funcsig">(<span class="funcname">vlist-&gt;safearray</span> vertices)</h3>
<pre class="fulldoc">Returns VERTICES as a safearray
VERTICES should be a list of lists of numbers.
VARS:
(VERTICES (LIST nil) (VL-EVERY 'POINT-P VERTICES))</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,182 @@
<!doctype html>
<html>
<head>
<title>util/document.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/document.lsp <a href="../../util/document.lsp">[src]</a></h2>
<pre class="fulldoc">Functions dealing with VLA Document objects</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-document.html#get-custom-dwgprop">(<span class="funcname">get-custom-dwgprop</span> key)</a></td>
<td>Returns value of custom drawing property KEY in current acadDoc</td>
</tr>
<tr>
<td class="funcsig"><a href="util-document.html#open-and">(<span class="funcname">open-and</span> filename readonly func-sym args)</a></td>
<td>Opens drawing FILENAME and executes function pointed to by FUNC-SYM with ARGS</td>
</tr>
<tr>
<td class="funcsig"><a href="util-document.html#osnap-off">(<span class="funcname">osnap-off</span>)</a></td>
<td>Suppress object snaps in current acadDoc and set *error* to restore them</td>
</tr>
<tr>
<td class="funcsig"><a href="util-document.html#osnap-on">(<span class="funcname">osnap-on</span>)</a></td>
<td>Restore object snaps in current acadDoc</td>
</tr>
<tr>
<td class="funcsig"><a href="util-document.html#reset-doc">(<span class="funcname">reset-doc</span>)</a></td>
<td>Resets acadDoc and modelSpace global vars</td>
</tr>
<tr>
<td class="funcsig"><a href="util-document.html#set-active-dimstyle">(<span class="funcname">set-active-dimstyle</span> name)</a></td>
<td>Sets active dimension style in current acadDoc to NAME</td>
</tr>
<tr>
<td class="funcsig"><a href="util-document.html#set-active-layer">(<span class="funcname">set-active-layer</span> name)</a></td>
<td>Sets active layer in current acadDoc to NAME</td>
</tr>
<tr>
<td class="funcsig"><a href="util-document.html#set-active-textstyle">(<span class="funcname">set-active-textstyle</span> name)</a></td>
<td>Sets active text style in current acadDoc to NAME</td>
</tr>
<tr>
<td class="funcsig"><a href="util-document.html#set-custom-dwgprop">(<span class="funcname">set-custom-dwgprop</span> key val)</a></td>
<td>Sets value of custom drawing property KEY to VAL</td>
</tr>
<tr>
<td class="funcsig"><a href="util-document.html#setup-env">(<span class="funcname">setup-env</span> 3d-p)</a></td>
<td>Ensure necessary layers, linetypes, and text styles are present</td>
</tr>
<tr>
<td class="funcsig"><a href="util-document.html#vla-getvar">(<span class="funcname">vla-getvar</span> sysvar)</a></td>
<td>Returns the value of SYSVAR in current acadDoc</td>
</tr>
<tr>
<td class="funcsig"><a href="util-document.html#vla-setvar">(<span class="funcname">vla-setvar</span> sysvar val)</a></td>
<td>Sets the value of SYSVAR in current acadDoc to VAL</td>
</tr>
</tbody>
</table>
<section>
<h3 id="get-custom-dwgprop" class="funcsig">(<span class="funcname">get-custom-dwgprop</span> key)</h3>
<pre class="fulldoc">Returns value of custom drawing property KEY in current acadDoc
VARS:
(KEY STR)</pre>
</section>
<section>
<h3 id="open-and" class="funcsig">(<span class="funcname">open-and</span> filename readonly func-sym args)</h3>
<pre class="fulldoc">Opens drawing FILENAME and executes function pointed to by FUNC-SYM with ARGS
If READONLY is non-nil, open the drawing in read-only mode. The drawing is automatically
closed and <a href="util-document.html#reset-doc">reset-doc</a> is called after.
VARS:
(FILENAME STR)
(FUNC-SYM SYM (FUNCTION-P (VL-SYMBOL-VALUE FUNC-SYM)))
(ARGS nil (LISTP ARGS))</pre>
</section>
<section>
<h3 id="osnap-off" class="funcsig">(<span class="funcname">osnap-off</span>)</h3>
<pre class="fulldoc">Suppress object snaps in current acadDoc and set *error* to restore them</pre>
</section>
<section>
<h3 id="osnap-on" class="funcsig">(<span class="funcname">osnap-on</span>)</h3>
<pre class="fulldoc">Restore object snaps in current acadDoc</pre>
</section>
<section>
<h3 id="reset-doc" class="funcsig">(<span class="funcname">reset-doc</span>)</h3>
<pre class="fulldoc">Resets acadDoc and modelSpace global vars</pre>
</section>
<section>
<h3 id="set-active-dimstyle" class="funcsig">(<span class="funcname">set-active-dimstyle</span> name)</h3>
<pre class="fulldoc">Sets active dimension style in current acadDoc to NAME
VARS:
(NAME STR)</pre>
</section>
<section>
<h3 id="set-active-layer" class="funcsig">(<span class="funcname">set-active-layer</span> name)</h3>
<pre class="fulldoc">Sets active layer in current acadDoc to NAME
VARS:
(NAME STR)</pre>
</section>
<section>
<h3 id="set-active-textstyle" class="funcsig">(<span class="funcname">set-active-textstyle</span> name)</h3>
<pre class="fulldoc">Sets active text style in current acadDoc to NAME
VARS:
(NAME STR)</pre>
</section>
<section>
<h3 id="set-custom-dwgprop" class="funcsig">(<span class="funcname">set-custom-dwgprop</span> key val)</h3>
<pre class="fulldoc">Sets value of custom drawing property KEY to VAL
VARS:
(KEY STR)
(VAL STR)</pre>
</section>
<section>
<h3 id="setup-env" class="funcsig">(<span class="funcname">setup-env</span> 3d-p)</h3>
<pre class="fulldoc">Ensure necessary layers, linetypes, and text styles are present
Includes different things if 3D-P is non-nil.</pre>
</section>
<section>
<h3 id="vla-getvar" class="funcsig">(<span class="funcname">vla-getvar</span> sysvar)</h3>
<pre class="fulldoc">Returns the value of SYSVAR in current acadDoc
VARS:
(SYSVAR (SYM STR))</pre>
</section>
<section>
<h3 id="vla-setvar" class="funcsig">(<span class="funcname">vla-setvar</span> sysvar val)</h3>
<pre class="fulldoc">Sets the value of SYSVAR in current acadDoc to VAL
VARS:
(SYSVAR (SYM STR))</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,279 @@
<!doctype html>
<html>
<head>
<title>util/documentation.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/documentation.lsp <a href="../../util/documentation.lsp">[src]</a></h2>
<pre class="fulldoc">Functions for documentation build system</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr><td colspan="3">Public</td></tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#defun-r">(<span class="funcname">defun-r</span> func-sym)</a></td>
<td>Registers the function FUNC-SYM points to</td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#doc-build">(<span class="funcname">doc-build</span>)</a></td>
<td>Bootstraps doc build process and opens a new blank drawing to carry it out</td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#doc-build-html">(<span class="funcname">doc-build-html</span>)</a></td>
<td>Builds HTML docs from files.json (generated by <a href="util-documentation.html#doc--write-json">doc--write-json</a>)</td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#docstring">(<span class="funcname">docstring</span> func-sym)</a></td>
<td>Prints the full docstring of the function FUNC-SYM points to</td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#whatis">(<span class="funcname">whatis</span> func-sym)</a></td>
<td>Prints the one-line description of the function FUNC-SYM points to</td>
</tr>
<tr><td colspan="3">Private</td></tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#defun-r--process-declare">(<span class="funcname">defun-r--process-declare</span> declare-form)</a></td>
<td>Returns a form for inclusion in the function being processed by <a href="util-documentation.html#defun-r">defun-r</a></td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#defun-r--process-declare-no-func-name">(<span class="funcname">defun-r--process-declare-no-func-name</span> _)</a></td>
<td>Tells <a href="util-documentation.html#defun-r">defun-r</a> not to advise the function to advertise its name</td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#defun-r--process-declare-tests">(<span class="funcname">defun-r--process-declare-tests</span> tests)</a></td>
<td>Adds tests for defining function. Nothing is added to function body</td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#defun-r--process-declare-var">(<span class="funcname">defun-r--process-declare-var</span> var-spec)</a></td>
<td>Processes a single form for <a href="util-documentation.html#defun-r--process-declare-vars">defun-r--process-declare-vars</a></td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#defun-r--process-declare-vars">(<span class="funcname">defun-r--process-declare-vars</span> vars-spec)</a></td>
<td>Returns function advice that checks vars in the eval environment</td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#defun-r--process-declare-with-data">(<span class="funcname">defun-r--process-declare-with-data</span> _)</a></td>
<td>Returns function advice that ensures this function was called inside a with-data call</td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#doc--add-docstring">(<span class="funcname">doc--add-docstring</span> func-sym docstring)</a></td>
<td>Adds DOCSTRING for FUNCSYM to global docstring vars</td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#doc--json-file-docs">(<span class="funcname">doc--json-file-docs</span> file-entry)</a></td>
<td>Returns file documentation as a JSON string</td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#doc--json-full-docs">(<span class="funcname">doc--json-full-docs</span>)</a></td>
<td>Returns the full documentation as a JSON string</td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#doc--json-func-docs">(<span class="funcname">doc--json-func-docs</span> func-entry)</a></td>
<td>Returns function documentation as a JSON string</td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#doc--print-docstring">(<span class="funcname">doc--print-docstring</span> func-sym which)</a></td>
<td>Print the docstring for FUNC-SYM from WHICH, which is either "short" or "long"</td>
</tr>
<tr>
<td class="funcsig"><a href="util-documentation.html#doc--write-json">(<span class="funcname">doc--write-json</span>)</a></td>
<td>Writes file and function docs to file</td>
</tr>
</tbody>
</table>
<section>
<h3 id="defun-r" class="funcsig">(<span class="funcname">defun-r</span> func-sym)</h3>
<pre class="fulldoc">Registers the function FUNC-SYM points to
Enables documentation generation and lookups with <a href="util-documentation.html#whatis">whatis</a> and <a href="util-documentation.html#docstring">docstring</a>. Replaces declare
forms in the function body. See <a href="util-documentation.html#defun-r--process-declare">defun-r--process-declare</a> for declare forms.
Redefines function using defun unless *no-redefun* is non-nil.
If *dev-mode* is nil, skip most of the process: only extract docstrings and declare forms,
then redefine the function.
If *inhibit-func-name* is nil, advise the function to set a variable *func-name* with its
name as a string.</pre>
</section>
<section>
<h3 id="doc-build" class="funcsig">(<span class="funcname">doc-build</span>)</h3>
<pre class="fulldoc">Bootstraps doc build process and opens a new blank drawing to carry it out</pre>
</section>
<section>
<h3 id="doc-build-html" class="funcsig">(<span class="funcname">doc-build-html</span>)</h3>
<pre class="fulldoc">Builds HTML docs from files.json (generated by <a href="util-documentation.html#doc--write-json">doc--write-json</a>)</pre>
</section>
<section>
<h3 id="docstring" class="funcsig">(<span class="funcname">docstring</span> func-sym)</h3>
<pre class="fulldoc">Prints the full docstring of the function FUNC-SYM points to
Returns nil if FUNC-SYM wasn't defined using the special documentation system.
VARS:
(FUNC-SYM (SYM))</pre>
</section>
<section>
<h3 id="whatis" class="funcsig">(<span class="funcname">whatis</span> func-sym)</h3>
<pre class="fulldoc">Prints the one-line description of the function FUNC-SYM points to
Returns nil if FUNC-SYM wasn't defined using the special documentation system.
VARS:
(FUNC-SYM (SYM))</pre>
</section>
<section>
<h3 id="defun-r--process-declare" class="funcsig">(<span class="funcname">defun-r--process-declare</span> declare-form)</h3>
<pre class="fulldoc">Returns a form for inclusion in the function being processed by <a href="util-documentation.html#defun-r">defun-r</a>
Each possible declare form has an expansion into a proper expression form, which is
inserted in place of the declare form in the function. Multiple declare forms are inserted
in the order specified. Declare form processors may modify the args, body, docstring, or
even name of the defining function, but should expressly declare doing so.
To define a new form, define a function named defun-r--process-declare-NAME, where NAME is
the first element of the form, e.g. <a href="util-documentation.html#defun-r--process-declare-vars">defun-r--process-declare-vars</a> is called when the form
is (vars ...). It will be called with a single arg: the cdr of the declare form. It should
return a form to be consed onto the front of the function's body. Processors may return
nil which will not be included in the function body. Put processor functions right after
this one in the file so they load as early as possible.
The entire declare form will be appended to the end of the function's docstring.
See the following for available declares:
- <a href="util-documentation.html#defun-r--process-declare-with-data">defun-r--process-declare-with-data</a>
- <a href="util-documentation.html#defun-r--process-declare-vars">defun-r--process-declare-vars</a>
- <a href="util-documentation.html#defun-r--process-declare-tests">defun-r--process-declare-tests</a></pre>
</section>
<section>
<h3 id="defun-r--process-declare-no-func-name" class="funcsig">(<span class="funcname">defun-r--process-declare-no-func-name</span> _)</h3>
<pre class="fulldoc">Tells <a href="util-documentation.html#defun-r">defun-r</a> not to advise the function to advertise its name</pre>
</section>
<section>
<h3 id="defun-r--process-declare-tests" class="funcsig">(<span class="funcname">defun-r--process-declare-tests</span> tests)</h3>
<pre class="fulldoc">Adds tests for defining function. Nothing is added to function body
If *test-build-p* is nil, nothing happens. Otherwise, all forms are gathered together into
a test function (see <a href="util-test.html#defun-t">defun-t</a>) that is automatically eval'd.
This is useful in that it adds expected behavior to docstrings automatically. Tests
specified this way should be short and easy to read -- ideally the function call wrapped
in an =/equal with its expected output. Tests that require setup/teardown should be
defined using <a href="util-test.html#defun-t">defun-t</a>.</pre>
</section>
<section>
<h3 id="defun-r--process-declare-var" class="funcsig">(<span class="funcname">defun-r--process-declare-var</span> var-spec)</h3>
<pre class="fulldoc">Processes a single form for <a href="util-documentation.html#defun-r--process-declare-vars">defun-r--process-declare-vars</a>
The car of VAR-SPEC is the name of the variable.
The cadr is a list of types. The type of the var must be one of the types. Specify nil for
the types list to allow any type (but still include validators). To say a var must be nil,
specify types as (nil).
If there are still elements in VAR-SPEC, they are validator forms. Each is evaluated and
if any return nil, throw an error.</pre>
</section>
<section>
<h3 id="defun-r--process-declare-vars" class="funcsig">(<span class="funcname">defun-r--process-declare-vars</span> vars-spec)</h3>
<pre class="fulldoc">Returns function advice that checks vars in the eval environment
VARS-SPEC should be a list of variable specifications for passing to
<a href="util-documentation.html#defun-r--process-declare-var">defun-r--process-declare-var</a>.</pre>
</section>
<section>
<h3 id="defun-r--process-declare-with-data" class="funcsig">(<span class="funcname">defun-r--process-declare-with-data</span> _)</h3>
<pre class="fulldoc">Returns function advice that ensures this function was called inside a with-data call</pre>
</section>
<section>
<h3 id="doc--add-docstring" class="funcsig">(<span class="funcname">doc--add-docstring</span> func-sym docstring)</h3>
<pre class="fulldoc">Adds DOCSTRING for FUNCSYM to global docstring vars
Adds the whole DOCSTRING to *docstrings-full* and the first line only to
*docstrings-short*. Both are entered as a cons cell of the form (FUNC-SYM . DOCSTRING).</pre>
</section>
<section>
<h3 id="doc--json-file-docs" class="funcsig">(<span class="funcname">doc--json-file-docs</span> file-entry)</h3>
<pre class="fulldoc">Returns file documentation as a JSON string</pre>
</section>
<section>
<h3 id="doc--json-full-docs" class="funcsig">(<span class="funcname">doc--json-full-docs</span>)</h3>
<pre class="fulldoc">Returns the full documentation as a JSON string</pre>
</section>
<section>
<h3 id="doc--json-func-docs" class="funcsig">(<span class="funcname">doc--json-func-docs</span> func-entry)</h3>
<pre class="fulldoc">Returns function documentation as a JSON string
VARS:
(FUNC-ENTRY LIST)
(FILE-NAME STR)</pre>
</section>
<section>
<h3 id="doc--print-docstring" class="funcsig">(<span class="funcname">doc--print-docstring</span> func-sym which)</h3>
<pre class="fulldoc">Print the docstring for FUNC-SYM from WHICH, which is either "short" or "long"
Will retrieve docstring from *docstrings-short* or *docstrings-long*, respectively. If
FUNC-SYM isn't valid, return nil.</pre>
</section>
<section>
<h3 id="doc--write-json" class="funcsig">(<span class="funcname">doc--write-json</span>)</h3>
<pre class="fulldoc">Writes file and function docs to file
All currently generated file and function documentation lives in the global variable
*file-docs*. Format its contents in JSON and write docs/code-manual/files.json.</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,121 @@
<!doctype html>
<html>
<head>
<title>util/error.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/error.lsp <a href="../../util/error.lsp">[src]</a></h2>
<pre class="fulldoc">Error-handling functions</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-error.html#assert">(<span class="funcname">assert</span> expr)</a></td>
<td>Throws an <a href="util-error.html#error">error</a> if EXPR evaluates to nil</td>
</tr>
<tr>
<td class="funcsig"><a href="util-error.html#assert-all">(<span class="funcname">assert-all</span> expr-lst)</a></td>
<td><a href="util-error.html#assert">assert</a> all expressions in EXPR-LST. If no errors, return a list of results</td>
</tr>
<tr>
<td class="funcsig"><a href="util-error.html#catch-all-error">(<span class="funcname">catch-all-error</span> attempt)</a></td>
<td>Wrapper for vl-catch-all-error-message</td>
</tr>
<tr>
<td class="funcsig"><a href="util-error.html#debug-print">(<span class="funcname">debug-print</span> msgs)</a></td>
<td>Prints debug messages when *dev-mode* is non-nil</td>
</tr>
<tr>
<td class="funcsig"><a href="util-error.html#debug-print-vars">(<span class="funcname">debug-print-vars</span> vars)</a></td>
<td>Uses <a href="util-error.html#debug-print">debug-print</a> to print symbol values</td>
</tr>
<tr>
<td class="funcsig"><a href="util-error.html#error">(<span class="funcname">error</span> msg)</a></td>
<td>Calls *error* if defined, prints MSG, and exits</td>
</tr>
</tbody>
</table>
<section>
<h3 id="assert" class="funcsig">(<span class="funcname">assert</span> expr)</h3>
<pre class="fulldoc">Throws an <a href="util-error.html#error">error</a> if EXPR evaluates to nil
If *assert-return-error* is non-nil, return the error string rather than
throwing. Otherwise, returns the value of EXPR.
NO-FUNC-NAME</pre>
</section>
<section>
<h3 id="assert-all" class="funcsig">(<span class="funcname">assert-all</span> expr-lst)</h3>
<pre class="fulldoc"><a href="util-error.html#assert">assert</a> all expressions in EXPR-LST. If no errors, return a list of results
If *ASSERT-RETURN-ERROR* is non-nil, catch the errors and return them as a list. If there
were no errors in this case, return nil.
NO-FUNC-NAME</pre>
</section>
<section>
<h3 id="catch-all-error" class="funcsig">(<span class="funcname">catch-all-error</span> attempt)</h3>
<pre class="fulldoc">Wrapper for vl-catch-all-error-message
This function returns *last-error* as the error message if the error was a quit/exit (which should indicate we threw it using <a href="util-error.html#error">error</a>).</pre>
</section>
<section>
<h3 id="debug-print" class="funcsig">(<span class="funcname">debug-print</span> msgs)</h3>
<pre class="fulldoc">Prints debug messages when *dev-mode* is non-nil
MSGS is a list of things that will be <a href="util-symbol.html#macro-expand">macro-expand</a>'d, turned into strings, concatenated,
and printed to console. A newline will be added to the front of the resulting string
automatically.</pre>
</section>
<section>
<h3 id="debug-print-vars" class="funcsig">(<span class="funcname">debug-print-vars</span> vars)</h3>
<pre class="fulldoc">Uses <a href="util-error.html#debug-print">debug-print</a> to print symbol values
VARS should be a list of symbols. Example use:
(setq *dev-mode* t a 1 b 2)
(debug-print-vars '(a b))
prints
A: 1
B: 2
NO-FUNC-NAME</pre>
</section>
<section>
<h3 id="error" class="funcsig">(<span class="funcname">error</span> msg)</h3>
<pre class="fulldoc">Calls *error* if defined, prints MSG, and exits
If *error-prefix* is defined, it is used as the message prefix. Otherwise, the default
[Error] is used.</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,132 @@
<!doctype html>
<html>
<head>
<title>util/file.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/file.lsp <a href="../../util/file.lsp">[src]</a></h2>
<pre class="fulldoc">File-handling functions</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-file.html#get-files-this-dir">(<span class="funcname">get-files-this-dir</span> full-path)</a></td>
<td>Prompts to select files from current directory using dialog listbox</td>
</tr>
<tr>
<td class="funcsig"><a href="util-file.html#open-csv">(<span class="funcname">open-csv</span> file)</a></td>
<td>Opens FILE with Excel</td>
</tr>
<tr>
<td class="funcsig"><a href="util-file.html#process-generic-def">(<span class="funcname">process-generic-def</span> def repo-file)</a></td>
<td>Retrieves the unique primary key for DEF in REPO-FILE</td>
</tr>
<tr>
<td class="funcsig"><a href="util-file.html#read-csv">(<span class="funcname">read-csv</span> file)</a></td>
<td>Return the contents of csv FILE as a list of lists of strings</td>
</tr>
<tr>
<td class="funcsig"><a href="util-file.html#read-file-to-string">(<span class="funcname">read-file-to-string</span> file)</a></td>
<td>Returns the contents of FILE as a string</td>
</tr>
<tr>
<td class="funcsig"><a href="util-file.html#read-key-val-file">(<span class="funcname">read-key-val-file</span> file)</a></td>
<td>Return the contents of key/value FILE as a list of 2-element lists</td>
</tr>
<tr>
<td class="funcsig"><a href="util-file.html#write-csv">(<span class="funcname">write-csv</span> lines file wrap-str)</a></td>
<td>Write the contents of LINES to FILE</td>
</tr>
</tbody>
</table>
<section>
<h3 id="get-files-this-dir" class="funcsig">(<span class="funcname">get-files-this-dir</span> full-path)</h3>
<pre class="fulldoc">Prompts to select files from current directory using dialog listbox
The DCL for this dialog is files-this-dir.dcl.</pre>
</section>
<section>
<h3 id="open-csv" class="funcsig">(<span class="funcname">open-csv</span> file)</h3>
<pre class="fulldoc">Opens FILE with Excel
VARS:
(FILE STR)</pre>
</section>
<section>
<h3 id="process-generic-def" class="funcsig">(<span class="funcname">process-generic-def</span> def repo-file)</h3>
<pre class="fulldoc">Retrieves the unique primary key for DEF in REPO-FILE
DEF should be the definition of an object as a list. REPO-FILE should be the name of a csv
file that serves as the database of that object type. Each unique object definition is
assigned a primary key number in the database. This function returns DEF with the primary
key as the first item.
This function handles creating REPO-FILE and assigning primary keys.
VARS:
(DEF LIST)
(REPO-FILE STR)</pre>
</section>
<section>
<h3 id="read-csv" class="funcsig">(<span class="funcname">read-csv</span> file)</h3>
<pre class="fulldoc">Return the contents of csv FILE as a list of lists of strings
VARS:
(FILE STR)</pre>
</section>
<section>
<h3 id="read-file-to-string" class="funcsig">(<span class="funcname">read-file-to-string</span> file)</h3>
<pre class="fulldoc">Returns the contents of FILE as a string
VARS:
(FILE STR)</pre>
</section>
<section>
<h3 id="read-key-val-file" class="funcsig">(<span class="funcname">read-key-val-file</span> file)</h3>
<pre class="fulldoc">Return the contents of key/value FILE as a list of 2-element lists
VARS:
(FILE STR)</pre>
</section>
<section>
<h3 id="write-csv" class="funcsig">(<span class="funcname">write-csv</span> lines file wrap-str)</h3>
<pre class="fulldoc">Write the contents of LINES to FILE
LINES should be a list of lists of strings. FILE should be a file name as a string. If
WRAP-STR is non-nil, wrap each cell in a formula like ="CELL-CONTENT".
VARS:
(LINES LIST)
(FILE STR)</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,149 @@
<!doctype html>
<html>
<head>
<title>util/function.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/function.lsp <a href="../../util/function.lsp">[src]</a></h2>
<pre class="fulldoc">Function-handling functions</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-function.html#apply-by-twos">(<span class="funcname">apply-by-twos</span> func lst)</a></td>
<td>Applies FUNC to adjacent elements of LST, accumulates and returns the results.</td>
</tr>
<tr>
<td class="funcsig"><a href="util-function.html#attempt-apply">(<span class="funcname">attempt-apply</span> func args)</a></td>
<td>If FUNC is a function, returns the result of applying it to ARGS. Else, return nil</td>
</tr>
<tr>
<td class="funcsig"><a href="util-function.html#compose">(<span class="funcname">compose</span> funcs-lst)</a></td>
<td>Returns a lambda that is the result of composing functions in FUNCS-LST</td>
</tr>
<tr>
<td class="funcsig"><a href="util-function.html#function-or-lambda-p">(<span class="funcname">function-or-lambda-p</span> x)</a></td>
<td>Returns nil unless X is a function or a lambda</td>
</tr>
<tr>
<td class="funcsig"><a href="util-function.html#function-p">(<span class="funcname">function-p</span> x)</a></td>
<td>Returns nil unless X is a function</td>
</tr>
<tr>
<td class="funcsig"><a href="util-function.html#map-apply">(<span class="funcname">map-apply</span> f map)</a></td>
<td>Returns the results of applying F to each element of MAP</td>
</tr>
<tr>
<td class="funcsig"><a href="util-function.html#map-apply!">(<span class="funcname">map-apply!</span> f map-sym)</a></td>
<td>Updates the list at MAP-SYM in place using <a href="util-function.html#map-apply">map-apply</a></td>
</tr>
</tbody>
</table>
<section>
<h3 id="apply-by-twos" class="funcsig">(<span class="funcname">apply-by-twos</span> func lst)</h3>
<pre class="fulldoc">Applies FUNC to adjacent elements of LST, accumulates and returns the results.
Example:
(apply-by-twos '+ '(1 2 3)) => '(3 5)
which is equivalently shown as
(list (+ 1 2) (+ 2 3))
VARS:
(FUNC nil (FUNCTION-OR-LAMBDA-P FUNC))
(LST nil (LISTP LST))
TESTS:
(EQUAL (APPLY-BY-TWOS '+ '(1 2 3)) '(3 5))</pre>
</section>
<section>
<h3 id="attempt-apply" class="funcsig">(<span class="funcname">attempt-apply</span> func args)</h3>
<pre class="fulldoc">If FUNC is a function, returns the result of applying it to ARGS. Else, return nil
VARS:
(FUNC)
(ARGS (LIST nil))</pre>
</section>
<section>
<h3 id="compose" class="funcsig">(<span class="funcname">compose</span> funcs-lst)</h3>
<pre class="fulldoc">Returns a lambda that is the result of composing functions in FUNCS-LST
FUNCS-LST should be a list of symbols pointing to single-argument functions. The resulting
function ill take a single argument and return the result of applying all the functions in
FUNCS-LST in the reverse of the order specified.
Example:
(compose '(lowercase to-string)) returns a function that take an argument and
returns the result of applying <a href="util-conversion.html#to-string">to-string</a> to it, followed by <a href="util-string.html#lowercase">lowercase</a>.</pre>
</section>
<section>
<h3 id="function-or-lambda-p" class="funcsig">(<span class="funcname">function-or-lambda-p</span> x)</h3>
<pre class="fulldoc">Returns nil unless X is a function or a lambda
TESTS:
(FUNCTION-OR-LAMBDA-P +)
(FUNCTION-OR-LAMBDA-P '(LAMBDA nil 1))
(NOT (FUNCTION-OR-LAMBDA-P 123))</pre>
</section>
<section>
<h3 id="function-p" class="funcsig">(<span class="funcname">function-p</span> x)</h3>
<pre class="fulldoc">Returns nil unless X is a function
TESTS:
(FUNCTION-P +)
(NOT (FUNCTION-P '(LAMBDA nil 1)))
(NOT (FUNCTION-P 123))</pre>
</section>
<section>
<h3 id="map-apply" class="funcsig">(<span class="funcname">map-apply</span> f map)</h3>
<pre class="fulldoc">Returns the results of applying F to each element of MAP
MAP should be a list of lists of arguments to F.
VARS:
(F nil (FUNCTION-OR-LAMBDA-P F))
(MAP nil (LISTP MAP))
TESTS:
(EQUAL (MAP-APPLY '+ '((1 2) (3 4))) '(3 7))</pre>
</section>
<section>
<h3 id="map-apply!" class="funcsig">(<span class="funcname">map-apply!</span> f map-sym)</h3>
<pre class="fulldoc">Updates the list at MAP-SYM in place using <a href="util-function.html#map-apply">map-apply</a>
VARS:
(F nil (FUNCTION-OR-LAMBDA-P F))
(MAP-SYM SYM (LISTP (VL-SYMBOL-VALUE MAP-SYM)))</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,436 @@
<!doctype html>
<html>
<head>
<title>util/geometry.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/geometry.lsp <a href="../../util/geometry.lsp">[src]</a></h2>
<pre class="fulldoc">Functions related to points, vectors, and planes</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-geometry.html#add-bulges">(<span class="funcname">add-bulges</span> pts)</a></td>
<td>Returns point list PTS with all points padded to 3D</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#angle-between">(<span class="funcname">angle-between</span> a1 a2)</a></td>
<td>Returns the angle between angles A1 and A2 (radians)</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#bend-deduct">(<span class="funcname">bend-deduct</span> thick ang rad k)</a></td>
<td>Calculates a bend deduction</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#calc-angles">(<span class="funcname">calc-angles</span> pts)</a></td>
<td>Returns a list of internal angles between PTS</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#calc-bulge">(<span class="funcname">calc-bulge</span> ang)</a></td>
<td>Returns the bulge value corresponding to a fillet of ANG radians</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#calc-fillet">(<span class="funcname">calc-fillet</span> pt1 pt2 pt3 radius)</a></td>
<td>Calculates a fillet for three 2D points at RADIUS</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#calc-fillet-3d">(<span class="funcname">calc-fillet-3d</span> pts-lst rad)</a></td>
<td>Returns a definition list formatted for use with <a href="util-object-3D.html#draw-fillet-3d">draw-fillet-3d</a> access with <a href="util-geometry.html#safe-calc-fillet-3d">safe-calc-fillet-3d</a></td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#calc-segs">(<span class="funcname">calc-segs</span> pts)</a></td>
<td>Returns a list of segment lengths between points in PTS</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#cross-product">(<span class="funcname">cross-product</span> v1 v2)</a></td>
<td>Returns the cross product of vectors V1 and V2</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#dihedral">(<span class="funcname">dihedral</span> v1 v2)</a></td>
<td>Returns the dihedral angle between two planes defined as normal vectors</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#displace-pt">(<span class="funcname">displace-pt</span> pt d)</a></td>
<td>Returns point PT displaced by point D</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#displace-pts">(<span class="funcname">displace-pts</span> pts d)</a></td>
<td>Returns points list PTS with all points displaced by point D</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#fillet-all-pts">(<span class="funcname">fillet-all-pts</span> pts radius)</a></td>
<td>Returns points list PTS with all corners filleted to RADIUS</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#int-ang">(<span class="funcname">int-ang</span> p1 p2 p3)</a></td>
<td>Return the internal angle of 3 points in radians</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#join-vertices">(<span class="funcname">join-vertices</span> lines)</a></td>
<td>Joins a list of lines</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#mid-2-pts">(<span class="funcname">mid-2-pts</span> p1 p2)</a></td>
<td>Returns a 2D point which is midway between P1 and P2</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#mirror-pts-h">(<span class="funcname">mirror-pts-h</span> pts y)</a></td>
<td>Returns points list PTS mirrored over horizontal line at Y</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#mirror-pts-v">(<span class="funcname">mirror-pts-v</span> pts x)</a></td>
<td>Returns points list PTS mirrored over vertical line at X</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#norm_3pts">(<span class="funcname">norm_3pts</span> org xdir ydir)</a></td>
<td>Returns a normal vector based on an origin x vector and y vector</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#normal-vec">(<span class="funcname">normal-vec</span> pt1 pt2 pt3)</a></td>
<td>Returns the 3D vector normal to 3 points</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#pipe-bend-lines">(<span class="funcname">pipe-bend-lines</span> segments-lst)</a></td>
<td>Returns the bend lines for a PSC pipe with segments SEGMENTS-LST</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#point-p">(<span class="funcname">point-p</span> pt)</a></td>
<td>Returns T if PT is a 2- or 3-item list of numbers</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#pts-list-p">(<span class="funcname">pts-list-p</span> pts-list)</a></td>
<td>Returns T if PTS-LIST is a list of points</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#rect-pts">(<span class="funcname">rect-pts</span> width height ins)</a></td>
<td>Returns the points list for a rectangle located at point INS</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#rezero-pts">(<span class="funcname">rezero-pts</span> pts v)</a></td>
<td>Returns points list PTS recentered on the Vth vertex</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#rotate-pt">(<span class="funcname">rotate-pt</span> pt ang)</a></td>
<td>Returns point PT rotated about the origin by ANG (radians)</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#rotate-pts">(<span class="funcname">rotate-pts</span> pts ang)</a></td>
<td>Return points list PTS with all points rotated about the orgin by ANG (radians)</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#safe-calc-fillet-3d">(<span class="funcname">safe-calc-fillet-3d</span> pts-lst rad)</a></td>
<td>Repeats <a href="util-geometry.html#calc-fillet-3d">calc-fillet-3d</a> with reduced radius until no error is returned</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#vec-">(<span class="funcname">vec-</span> v1 v2)</a></td>
<td>Returns the result of subtracting vector V1 from V2</td>
</tr>
<tr>
<td class="funcsig"><a href="util-geometry.html#within-box-p">(<span class="funcname">within-box-p</span> pt boxpt1 boxpt2)</a></td>
<td>Returns T if PT is within the box defined by BOXPT1 and BOXPT2</td>
</tr>
</tbody>
</table>
<section>
<h3 id="add-bulges" class="funcsig">(<span class="funcname">add-bulges</span> pts)</h3>
<pre class="fulldoc">Returns point list PTS with all points padded to 3D</pre>
</section>
<section>
<h3 id="angle-between" class="funcsig">(<span class="funcname">angle-between</span> a1 a2)</h3>
<pre class="fulldoc">Returns the angle between angles A1 and A2 (radians)</pre>
</section>
<section>
<h3 id="bend-deduct" class="funcsig">(<span class="funcname">bend-deduct</span> thick ang rad k)</h3>
<pre class="fulldoc">Calculates a bend deduction
THICK is the material thickness. ANG is the excluded bend angle in degrees. RAD is the
bend radius. K is the K-value.</pre>
</section>
<section>
<h3 id="calc-angles" class="funcsig">(<span class="funcname">calc-angles</span> pts)</h3>
<pre class="fulldoc">Returns a list of internal angles between PTS</pre>
</section>
<section>
<h3 id="calc-bulge" class="funcsig">(<span class="funcname">calc-bulge</span> ang)</h3>
<pre class="fulldoc">Returns the bulge value corresponding to a fillet of ANG radians</pre>
</section>
<section>
<h3 id="calc-fillet" class="funcsig">(<span class="funcname">calc-fillet</span> pt1 pt2 pt3 radius)</h3>
<pre class="fulldoc">Calculates a fillet for three 2D points at RADIUS
All points should be 2D. The return value will be a list of four 3D points, with the Z
coordinate representing the bulge.
VARS:
(PT1 LIST (POINT-P PT1))
(PT2 LIST (POINT-P PT2))
(PT3 LIST (POINT-P PT3))
(RADIUS nil (NUMBERP RADIUS))</pre>
</section>
<section>
<h3 id="calc-fillet-3d" class="funcsig">(<span class="funcname">calc-fillet-3d</span> pts-lst rad)</h3>
<pre class="fulldoc">Returns a definition list formatted for use with <a href="util-object-3D.html#draw-fillet-3d">draw-fillet-3d</a> access with <a href="util-geometry.html#safe-calc-fillet-3d">safe-calc-fillet-3d</a></pre>
</section>
<section>
<h3 id="calc-segs" class="funcsig">(<span class="funcname">calc-segs</span> pts)</h3>
<pre class="fulldoc">Returns a list of segment lengths between points in PTS
Holds the minimum starting segment at 6" and the ending segment at 18" for pipe
bending.
VARS:
(PTS nil (LISTP PTS) (VL-EVERY 'POINT-P PTS))</pre>
</section>
<section>
<h3 id="cross-product" class="funcsig">(<span class="funcname">cross-product</span> v1 v2)</h3>
<pre class="fulldoc">Returns the cross product of vectors V1 and V2</pre>
</section>
<section>
<h3 id="dihedral" class="funcsig">(<span class="funcname">dihedral</span> v1 v2)</h3>
<pre class="fulldoc">Returns the dihedral angle between two planes defined as normal vectors</pre>
</section>
<section>
<h3 id="displace-pt" class="funcsig">(<span class="funcname">displace-pt</span> pt d)</h3>
<pre class="fulldoc">Returns point PT displaced by point D
VARS:
(PT nil (POINT-P PT))
(D nil (POINT-P D))
TESTS:
(EQUAL (DISPLACE-PT '(0 0) '(2 2)) '(2 2))
(EQUAL (DISPLACE-PT '(1 2) '(3 4)) '(4 6))</pre>
</section>
<section>
<h3 id="displace-pts" class="funcsig">(<span class="funcname">displace-pts</span> pts d)</h3>
<pre class="fulldoc">Returns points list PTS with all points displaced by point D
VARS:
(PTS nil (PTS-LIST-P PTS))
(D nil (POINT-P D))
TESTS:
(EQUAL (DISPLACE-PTS '((0 0) (1 1)) '(2 2)) '((2 2) (3 3)))</pre>
</section>
<section>
<h3 id="fillet-all-pts" class="funcsig">(<span class="funcname">fillet-all-pts</span> pts radius)</h3>
<pre class="fulldoc">Returns points list PTS with all corners filleted to RADIUS
See <a href="util-geometry.html#calc-fillet">calc-fillet</a> for individual fillet calculations.</pre>
</section>
<section>
<h3 id="int-ang" class="funcsig">(<span class="funcname">int-ang</span> p1 p2 p3)</h3>
<pre class="fulldoc">Return the internal angle of 3 points in radians</pre>
</section>
<section>
<h3 id="join-vertices" class="funcsig">(<span class="funcname">join-vertices</span> lines)</h3>
<pre class="fulldoc">Joins a list of lines
LINES should be a list of lines (lists of points). If the last point of one line equals the
first point of the next, they are joined into one. Otherwise, they are left separate.
VARS:
(LINES nil (LISTP LINES) (VL-EVERY '(LAMBDA (LINE) (VL-EVERY 'POINT-P LINE)) LINES))
TESTS:
(EQUAL (JOIN-VERTICES '(((0 0) (1 0)) ((1 0) (1 1)) ((1 2) (0 2)))) '(((0 0) (1 0) (1 1)) ((1 2) (0 2))))</pre>
</section>
<section>
<h3 id="mid-2-pts" class="funcsig">(<span class="funcname">mid-2-pts</span> p1 p2)</h3>
<pre class="fulldoc">Returns a 2D point which is midway between P1 and P2</pre>
</section>
<section>
<h3 id="mirror-pts-h" class="funcsig">(<span class="funcname">mirror-pts-h</span> pts y)</h3>
<pre class="fulldoc">Returns points list PTS mirrored over horizontal line at Y
VARS:
(PTS LIST (VL-EVERY 'POINT-P PTS))
(Y nil (NUMBERP Y))
TESTS:
(EQUAL (MIRROR-PTS-H '((0 0) (1 1)) 0) '((0 0) (1 -1)))</pre>
</section>
<section>
<h3 id="mirror-pts-v" class="funcsig">(<span class="funcname">mirror-pts-v</span> pts x)</h3>
<pre class="fulldoc">Returns points list PTS mirrored over vertical line at X
VARS:
(PTS LIST (VL-EVERY 'POINT-P PTS))
(X nil (NUMBERP X))
TESTS:
(EQUAL (MIRROR-PTS-V '((0 0) (1 1)) 0) '((0 0) (-1 1)))</pre>
</section>
<section>
<h3 id="norm_3pts" class="funcsig">(<span class="funcname">norm_3pts</span> org xdir ydir)</h3>
<pre class="fulldoc">Returns a normal vector based on an origin x vector and y vector
VARS:
(ORG LIST POINT-P)
(XDIR LIST POINT-P)
(YDIR LIST POINT-P)</pre>
</section>
<section>
<h3 id="normal-vec" class="funcsig">(<span class="funcname">normal-vec</span> pt1 pt2 pt3)</h3>
<pre class="fulldoc">Returns the 3D vector normal to 3 points</pre>
</section>
<section>
<h3 id="pipe-bend-lines" class="funcsig">(<span class="funcname">pipe-bend-lines</span> segments-lst)</h3>
<pre class="fulldoc">Returns the bend lines for a PSC pipe with segments SEGMENTS-LST
Each element to SEGMENTS-LST should be a string. Assumes 1.66OD pipe, centerline radius of
2.83" and k value of 0.18.</pre>
</section>
<section>
<h3 id="point-p" class="funcsig">(<span class="funcname">point-p</span> pt)</h3>
<pre class="fulldoc">Returns T if PT is a 2- or 3-item list of numbers</pre>
</section>
<section>
<h3 id="pts-list-p" class="funcsig">(<span class="funcname">pts-list-p</span> pts-list)</h3>
<pre class="fulldoc">Returns T if PTS-LIST is a list of points</pre>
</section>
<section>
<h3 id="rect-pts" class="funcsig">(<span class="funcname">rect-pts</span> width height ins)</h3>
<pre class="fulldoc">Returns the points list for a rectangle located at point INS
VARS:
(WIDTH nil (NUMBERP WIDTH))
(HEIGHT nil (NUMBERP HEIGHT))
(INS LIST (POINT-P INS))
TESTS:
(EQUAL (RECT-PTS 1 1 '(0 0)) '((0 0) (1 0) (1 1) (0 1)))
(EQUAL (RECT-PTS 1 2 '(3 4)) '((3 4) (4 4) (4 6) (3 6)))</pre>
</section>
<section>
<h3 id="rezero-pts" class="funcsig">(<span class="funcname">rezero-pts</span> pts v)</h3>
<pre class="fulldoc">Returns points list PTS recentered on the Vth vertex
Rotates the list around until V is the 0th item and displaces the points so they are
correct. Adds Z coordinates if 2D.
VARS:
(PTS nil (PTS-LIST-P PTS))
(V INT (>= V 0) (<= V (LENGTH PTS)))
TESTS:
(EQUAL (REZERO-PTS '((0 0) (1 1)) 1) '((0 0 0) (-1 -1 0)))</pre>
</section>
<section>
<h3 id="rotate-pt" class="funcsig">(<span class="funcname">rotate-pt</span> pt ang)</h3>
<pre class="fulldoc">Returns point PT rotated about the origin by ANG (radians)
Adds Z coordinate if 2D.
VARS:
(PT nil (POINT-P PT))
(ANG nil (NUMBERP ANG))</pre>
</section>
<section>
<h3 id="rotate-pts" class="funcsig">(<span class="funcname">rotate-pts</span> pts ang)</h3>
<pre class="fulldoc">Return points list PTS with all points rotated about the orgin by ANG (radians)
Adds Z coordinates if 2D.</pre>
</section>
<section>
<h3 id="safe-calc-fillet-3d" class="funcsig">(<span class="funcname">safe-calc-fillet-3d</span> pts-lst rad)</h3>
<pre class="fulldoc">Repeats <a href="util-geometry.html#calc-fillet-3d">calc-fillet-3d</a> with reduced radius until no error is returned
VARS:
(PTS-LST LIST (VL-EVERY 'POINT-P PTS-LST) (VL-EVERY 'CDDR PTS-LST))
(RAD nil (NUMBERP RAD))</pre>
</section>
<section>
<h3 id="vec-" class="funcsig">(<span class="funcname">vec-</span> v1 v2)</h3>
<pre class="fulldoc">Returns the result of subtracting vector V1 from V2</pre>
</section>
<section>
<h3 id="within-box-p" class="funcsig">(<span class="funcname">within-box-p</span> pt boxpt1 boxpt2)</h3>
<pre class="fulldoc">Returns T if PT is within the box defined by BOXPT1 and BOXPT2
VARS:
(PT LIST (POINT-P PT))
(BOXPT1 LIST (POINT-P BOXPT1))
(BOXPT2 LIST (POINT-P BOXPT2))
TESTS:
(WITHIN-BOX-P '(0 0) '(-1 -1) '(1 1))
(NOT (WITHIN-BOX-P '(10 10) '(0 0) '(1 1)))</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,50 @@
<!doctype html>
<html>
<head>
<title>util/job.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/job.lsp <a href="../../util/job.lsp">[src]</a></h2>
<pre class="fulldoc">Functions related to the job directory</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-job.html#find-job-dir">(<span class="funcname">find-job-dir</span>)</a></td>
<td>Returns the root job directory if we are a descendant of one</td>
</tr>
<tr>
<td class="funcsig"><a href="util-job.html#get-job-info">(<span class="funcname">get-job-info</span>)</a></td>
<td>Return the current job info as a data list</td>
</tr>
</tbody>
</table>
<section>
<h3 id="find-job-dir" class="funcsig">(<span class="funcname">find-job-dir</span>)</h3>
<pre class="fulldoc">Returns the root job directory if we are a descendant of one</pre>
</section>
<section>
<h3 id="get-job-info" class="funcsig">(<span class="funcname">get-job-info</span>)</h3>
<pre class="fulldoc">Return the current job info as a data list</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,90 @@
<!doctype html>
<html>
<head>
<title>util/json.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/json.lsp <a href="../../util/json.lsp">[src]</a></h2>
<pre class="fulldoc">Functions for creating JSON lists
See the following for usage:
- <a href="util-documentation.html#doc--json-full-docs">doc--json-full-docs</a>
- <a href="util-documentation.html#doc--json-file-docs">doc--json-file-docs</a>
- <a href="util-documentation.html#doc--json-func-docs">doc--json-func-docs</a></pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-json.html#json-array-prop">(<span class="funcname">json-array-prop</span> key value)</a></td>
<td>Return KEY and VALUE as a JSON array property</td>
</tr>
<tr>
<td class="funcsig"><a href="util-json.html#json-object">(<span class="funcname">json-object</span> props)</a></td>
<td>Combine the JSON properties in PROPS into an object and return it</td>
</tr>
<tr>
<td class="funcsig"><a href="util-json.html#json-prop">(<span class="funcname">json-prop</span> key value)</a></td>
<td>Return KEY and VALUE as a JSON property</td>
</tr>
<tr>
<td class="funcsig"><a href="util-json.html#json-string-prop">(<span class="funcname">json-string-prop</span> key value)</a></td>
<td>Return KEY and VALUE as a JSON string property</td>
</tr>
</tbody>
</table>
<section>
<h3 id="json-array-prop" class="funcsig">(<span class="funcname">json-array-prop</span> key value)</h3>
<pre class="fulldoc">Return KEY and VALUE as a JSON array property
VARS:
(KEY STR)
(VALUE LIST (VL-EVERY 'STRINGP VALUE))</pre>
</section>
<section>
<h3 id="json-object" class="funcsig">(<span class="funcname">json-object</span> props)</h3>
<pre class="fulldoc">Combine the JSON properties in PROPS into an object and return it
VARS:
(PROPS LIST (VL-EVERY 'STRINGP PROPS))</pre>
</section>
<section>
<h3 id="json-prop" class="funcsig">(<span class="funcname">json-prop</span> key value)</h3>
<pre class="fulldoc">Return KEY and VALUE as a JSON property
VARS:
(KEY STR)
(VALUE STR)</pre>
</section>
<section>
<h3 id="json-string-prop" class="funcsig">(<span class="funcname">json-string-prop</span> key value)</h3>
<pre class="fulldoc">Return KEY and VALUE as a JSON string property
VARS:
(KEY STR)
(VALUE STR)</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,741 @@
<!doctype html>
<html>
<head>
<title>util/list.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/list.lsp <a href="../../util/list.lsp">[src]</a></h2>
<pre class="fulldoc">List-handling functions</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-list.html#2-item-list">(<span class="funcname">2-item-list</span> lst)</a></td>
<td>Returns the elements of LST grouped by twos</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#3-item-list">(<span class="funcname">3-item-list</span> lst)</a></td>
<td>Returns the elements of LST grouped by threes</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#add-to-alist">(<span class="funcname">add-to-alist</span> alist-sym key value append-p)</a></td>
<td>Adds an entry for KEY to ALIST pointed to by ALIST-SYM</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#add-to-list">(<span class="funcname">add-to-list</span> list-sym elt)</a></td>
<td>Adds ELT to list referred to by LIST-SYM</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#add-to-list-at-pos">(<span class="funcname">add-to-list-at-pos</span> lst-sym elt pos)</a></td>
<td>Adds ELT to list referred to by LST-SYM at POS</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#append!">(<span class="funcname">append!</span> lst-sym app-lst)</a></td>
<td>Updates LST-SYM in place using append</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#circular-nth">(<span class="funcname">circular-nth</span> n lst)</a></td>
<td>Wrapper for nth that treats LST as periodic and infinite</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#circular-shift">(<span class="funcname">circular-shift</span> lst n)</a></td>
<td>Returns LST left-shifted N times</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#cons-ind-lst">(<span class="funcname">cons-ind-lst</span> len i)</a></td>
<td>Returns a range of integers starting at I (optional) of length LEN, by 1</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#count-items">(<span class="funcname">count-items</span> ci-lst)</a></td>
<td>Returns a list of counts of items in CI-LST</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#filter">(<span class="funcname">filter</span> lst f)</a></td>
<td>Returns LST without elements for which F returns nil</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#filter!">(<span class="funcname">filter!</span> lst-sym f)</a></td>
<td>Updates list at LST-STM in place using <a href="util-list.html#filter">filter</a></td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#find-first">(<span class="funcname">find-first</span> pred lst)</a></td>
<td>Returns the first element in LST for which PRED returns non-nil</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#first-two">(<span class="funcname">first-two</span> lst)</a></td>
<td>Returns the first two elements of LST</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#lst*">(<span class="funcname">lst*</span> lstelt n)</a></td>
<td>Returns a list consisting of ELE repeated N times</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#map-append">(<span class="funcname">map-append</span> f lst)</a></td>
<td>Alias for (apply 'append (mapcar f lst))</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#mapcar!">(<span class="funcname">mapcar!</span> f lst-sym)</a></td>
<td>Updates list at LST-SYM in place using mapcar</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#member*">(<span class="funcname">member*</span> target list2)</a></td>
<td>Returns T if any members of TARGET appear in LST</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#pop!">(<span class="funcname">pop!</span> pop-lst-sym)</a></td>
<td>Sets LST-SYM to (cdr LST) and returns (car LST)</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#range">(<span class="funcname">range</span> from to by)</a></td>
<td>Returns a list of numbers that range from FROM to TO by step BY</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#rcons">(<span class="funcname">rcons</span> lst x)</a></td>
<td>Returns LST with X added to the end</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#remove">(<span class="funcname">remove</span> lst f)</a></td>
<td>Returns LST with only elements for which F returns nil</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#remove!">(<span class="funcname">remove!</span> lst-sym f)</a></td>
<td>Updates list at LST-STM in place using <a href="util-list.html#remove">remove</a></td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#remove-last">(<span class="funcname">remove-last</span> lst)</a></td>
<td>Returns LST without its last element</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#remove-nth">(<span class="funcname">remove-nth</span> n lst)</a></td>
<td>Returns LST with Nth item removed</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#rpt-lst">(<span class="funcname">rpt-lst</span> n ele)</a></td>
<td>Returns a list consisting of ELE repeated N times</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#safe-nth">(<span class="funcname">safe-nth</span> n lst)</a></td>
<td>Wrapper for nth that returns nil when LST is nil</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#slice">(<span class="funcname">slice</span> lst start len)</a></td>
<td>Return a slice from LST of length LEN starting at START</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#sort!">(<span class="funcname">sort!</span> lst-sym f)</a></td>
<td>Sorts LST in place using vl-sort</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#split-list">(<span class="funcname">split-list</span> lst f true-lst-sym false-lst-sym)</a></td>
<td>Puts items for which F returns nil in FALSE-LST and the rest in TRUE-LST</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#split-list-n">(<span class="funcname">split-list-n</span> lst n first-lst-sym second-lst-sym)</a></td>
<td>Splits LST on index N, putting first half in FIRST-LST and the rest in SECOND-LST</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#strip-nil">(<span class="funcname">strip-nil</span> lst)</a></td>
<td>Returns LST with nil elements removed</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#subst!">(<span class="funcname">subst!</span> new old lst-sym)</a></td>
<td>Updates list at LST-SYM in place using subst</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#take">(<span class="funcname">take</span> n lst)</a></td>
<td>Returns the first N elements of LST</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#take!">(<span class="funcname">take!</span> n take-lst-sym)</a></td>
<td>Returns the first N elements from TAKE-LST-SYM, removing them</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#take-while">(<span class="funcname">take-while</span> predicate lst)</a></td>
<td>Returns the first elements of LST for which PREDICATE is true</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#test-for">(<span class="funcname">test-for</span> test-type lst)</a></td>
<td>Returns T if all elements of LST are of type TEST-TYPE</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#uniquify">(<span class="funcname">uniquify</span> lst)</a></td>
<td>Returns LST without duplicate items</td>
</tr>
<tr>
<td class="funcsig"><a href="util-list.html#zip">(<span class="funcname">zip</span> l1 l2)</a></td>
<td>Returns lists L1 and L2 interleaved</td>
</tr>
</tbody>
</table>
<section>
<h3 id="2-item-list" class="funcsig">(<span class="funcname">2-item-list</span> lst)</h3>
<pre class="fulldoc">Returns the elements of LST grouped by twos
VARS:
(LST nil (LISTP LST))
TESTS:
(EQUAL (2-ITEM-LIST '(1 2 A B)) '((1 2) (A B)))
(EQUAL (2-ITEM-LIST '(1 2 A)) '((1 2) (A nil)))</pre>
</section>
<section>
<h3 id="3-item-list" class="funcsig">(<span class="funcname">3-item-list</span> lst)</h3>
<pre class="fulldoc">Returns the elements of LST grouped by threes
VARS:
(LST nil (LISTP LST))
TESTS:
(EQUAL (3-ITEM-LIST '(1 2 3 A B C)) '((1 2 3) (A B C)))
(EQUAL (3-ITEM-LIST '(1 2 3 A B)) '((1 2 3) (A B nil)))</pre>
</section>
<section>
<h3 id="add-to-alist" class="funcsig">(<span class="funcname">add-to-alist</span> alist-sym key value append-p)</h3>
<pre class="fulldoc">Adds an entry for KEY to ALIST pointed to by ALIST-SYM
If ALIST already contains an entry for KEY, replace it. If so, and the following
conditions are met, append to the value of the entry rather than replacing:
1. APPEND-P must be non-nil
2. Original entry value and new value types must match
3. Types must be STR or LIST
VARS:
(ALIST-SYM SYM (SYM-LST-P ALIST-SYM))
TESTS:
(EQUAL ((LAMBDA (/ LST) (ADD-TO-ALIST 'LST 'A 1 nil))) '((A . 1)))
(EQUAL ((LAMBDA (LST) (ADD-TO-ALIST 'LST 'A 1 nil)) '((A . 2))) '((A . 1)))
(EQUAL ((LAMBDA (LST) (ADD-TO-ALIST 'LST 'A ", world" T)) '((A . "hello"))) '((A . "hello, world")))</pre>
</section>
<section>
<h3 id="add-to-list" class="funcsig">(<span class="funcname">add-to-list</span> list-sym elt)</h3>
<pre class="fulldoc">Adds ELT to list referred to by LIST-SYM
VARS:
(LIST-SYM SYM (SYM-LST-P LIST-SYM))
TESTS:
(EQUAL ((LAMBDA (/ LST) (ADD-TO-LIST 'LST 1))) '(1))
(EQUAL ((LAMBDA (LST) (ADD-TO-LIST 'LST 2)) '(1)) '(1 2))</pre>
</section>
<section>
<h3 id="add-to-list-at-pos" class="funcsig">(<span class="funcname">add-to-list-at-pos</span> lst-sym elt pos)</h3>
<pre class="fulldoc">Adds ELT to list referred to by LST-SYM at POS
VARS:
(LST-SYM SYM (SYM-LST-P LST-SYM))
(POS INT)
TESTS:
(EQUAL ((LAMBDA (LST) (ADD-TO-LIST-AT-POS 'LST nil 1)) '(1 2)) '(1 nil 2))</pre>
</section>
<section>
<h3 id="append!" class="funcsig">(<span class="funcname">append!</span> lst-sym app-lst)</h3>
<pre class="fulldoc">Updates LST-SYM in place using append
VARS:
(LST-SYM SYM (SYM-LST-P LST-SYM))
(APP-LST nil (LISTP APP-LST))
TESTS:
(EQUAL ((LAMBDA (LST) (APPEND! 'LST '(3)) LST) '(1 2)) '(1 2 3))</pre>
</section>
<section>
<h3 id="circular-nth" class="funcsig">(<span class="funcname">circular-nth</span> n lst)</h3>
<pre class="fulldoc">Wrapper for nth that treats LST as periodic and infinite
If a negative is supplied for N, index from the end of LST. If N is greater than the
length of LST, wrap around to the beginning again as many times as necessary.
VARS:
(N INT)
(LST nil (LISTP LST))
TESTS:
(= (CIRCULAR-NTH 1 '(1 2 3)) 2)
(= (CIRCULAR-NTH -1 '(1 2 3)) 3)
(= (CIRCULAR-NTH 3 '(1 2 3)) 1)
(= (CIRCULAR-NTH 6 '(1 2 3)) 1)</pre>
</section>
<section>
<h3 id="circular-shift" class="funcsig">(<span class="funcname">circular-shift</span> lst n)</h3>
<pre class="fulldoc">Returns LST left-shifted N times
VARS:
(LST nil (LISTP LST))
(N INT)
TESTS:
(EQUAL (CIRCULAR-SHIFT '(1 2 3) 1) '(2 3 1))
(EQUAL (CIRCULAR-SHIFT '(1 2 3) 2) '(3 1 2))</pre>
</section>
<section>
<h3 id="cons-ind-lst" class="funcsig">(<span class="funcname">cons-ind-lst</span> len i)</h3>
<pre class="fulldoc">Returns a range of integers starting at I (optional) of length LEN, by 1
VARS:
(LEN INT)
(I (INT nil))
TESTS:
(EQUAL (CONS-IND-LST 3 nil) '(0 1 2))
(EQUAL (CONS-IND-LST 3 5) '(5 6 7))</pre>
</section>
<section>
<h3 id="count-items" class="funcsig">(<span class="funcname">count-items</span> ci-lst)</h3>
<pre class="fulldoc">Returns a list of counts of items in CI-LST
Each itme in the return list is a 2-item list whose car is the element and whose cadr is
the count of that element in CI-LST.
VARS:
(CI-LST nil (LISTP CI-LST))
TESTS:
(EQUAL (COUNT-ITEMS '(1)) '((1 1)))
(EQUAL (COUNT-ITEMS '(1 1 2 2 2)) '((1 2) (2 3)))</pre>
</section>
<section>
<h3 id="filter" class="funcsig">(<span class="funcname">filter</span> lst f)</h3>
<pre class="fulldoc">Returns LST without elements for which F returns nil
F should be a quoted list like '(= X 1), where X is the element in question. F will be
processed by <a href="util-symbol.html#macro-expand">macro-expand</a>.
VARS:
(LST nil (LISTP LST))
(F nil (LISTP F))
TESTS:
(EQUAL (FILTER '(1 2 3) '(< X 2)) '(1))
(EQUAL (FILTER '(1 2 3) '(> X 0)) '(1 2 3))</pre>
</section>
<section>
<h3 id="filter!" class="funcsig">(<span class="funcname">filter!</span> lst-sym f)</h3>
<pre class="fulldoc">Updates list at LST-STM in place using <a href="util-list.html#filter">filter</a>
VARS:
(LST-SYM SYM (SYM-LST-P LST-SYM))
(F nil (LISTP F))</pre>
</section>
<section>
<h3 id="find-first" class="funcsig">(<span class="funcname">find-first</span> pred lst)</h3>
<pre class="fulldoc">Returns the first element in LST for which PRED returns non-nil
PRED should be a function of one argument.
VARS:
(PRED nil (FUNCTION-OR-LAMBDA-P PRED))
(LST nil (LISTP LST))
TESTS:
(= (FIND-FIRST '(LAMBDA (A) (> A 1)) '(1 2 3)) 2)</pre>
</section>
<section>
<h3 id="first-two" class="funcsig">(<span class="funcname">first-two</span> lst)</h3>
<pre class="fulldoc">Returns the first two elements of LST
VARS:
(LST nil (LISTP LST))
TESTS:
(EQUAL (FIRST-TWO '(1 2 3)) '(1 2))</pre>
</section>
<section>
<h3 id="lst*" class="funcsig">(<span class="funcname">lst*</span> lstelt n)</h3>
<pre class="fulldoc">Returns a list consisting of ELE repeated N times
Duplicate (args reversed) of <a href="util-list.html#rpt-lst">rpt-lst</a>.
VARS:
(N INT)
TESTS:
(EQUAL (LST* 'Q 3) '(Q Q Q))</pre>
</section>
<section>
<h3 id="map-append" class="funcsig">(<span class="funcname">map-append</span> f lst)</h3>
<pre class="fulldoc">Alias for (apply 'append (mapcar f lst))
VARS:
(F nil (FUNCTION-OR-LAMBDA-P F))
(LST nil (LISTP LST))
TESTS:
(EQUAL (MAP-APPEND 'REVERSE '((1 2) (3 4))) '(2 1 4 3))</pre>
</section>
<section>
<h3 id="mapcar!" class="funcsig">(<span class="funcname">mapcar!</span> f lst-sym)</h3>
<pre class="fulldoc">Updates list at LST-SYM in place using mapcar
VARS:
(F nil (FUNCTION-OR-LAMBDA-P F))
(LST-SYM SYM (SYM-LST-P LST-SYM))
TESTS:
(EQUAL ((LAMBDA (LST) (MAPCAR! '1+ 'LST) LST) '(1 2 3)) '(2 3 4))</pre>
</section>
<section>
<h3 id="member*" class="funcsig">(<span class="funcname">member*</span> target list2)</h3>
<pre class="fulldoc">Returns T if any members of TARGET appear in LST
TARGET can be a list or atom.
VARS:
(LIST2 nil (LISTP LIST2))
TESTS:
(MEMBER 'A '(A B))
(NOT (MEMBER 'C '(A B)))
(MEMBER* '(A 1) '(A B))
(MEMBER* '(A 1) '(1 2))
(NOT (MEMBER* '(A B) '(1 2)))</pre>
</section>
<section>
<h3 id="pop!" class="funcsig">(<span class="funcname">pop!</span> pop-lst-sym)</h3>
<pre class="fulldoc">Sets LST-SYM to (cdr LST) and returns (car LST)
VARS:
(POP-LST-SYM SYM)
TESTS:
(EQUAL ((LAMBDA (LST) (POP! 'LST)) nil) nil)
(EQUAL ((LAMBDA (LST) (POP! 'LST)) '(1)) 1)
(EQUAL ((LAMBDA (LST) (POP! 'LST) LST) '(1 2)) '(2))</pre>
</section>
<section>
<h3 id="range" class="funcsig">(<span class="funcname">range</span> from to by)</h3>
<pre class="fulldoc">Returns a list of numbers that range from FROM to TO by step BY
TO is exclusive
VARS:
(FROM nil (NUMBERP FROM))
(TO nil (NUMBERP TO))
(BY nil (NUMBERP BY))
TESTS:
(EQUAL (RANGE 1 4 1) '(1 2 3))
(EQUAL (RANGE 0.75 1.25 0.125) '(0.75 0.875 1.0 1.125))</pre>
</section>
<section>
<h3 id="rcons" class="funcsig">(<span class="funcname">rcons</span> lst x)</h3>
<pre class="fulldoc">Returns LST with X added to the end
VARS:
(LST nil (LISTP LST))
TESTS:
(EQUAL (RCONS '(1) 2) '(1 2))
(EQUAL (RCONS nil 1) '(1))</pre>
</section>
<section>
<h3 id="remove" class="funcsig">(<span class="funcname">remove</span> lst f)</h3>
<pre class="fulldoc">Returns LST with only elements for which F returns nil
F should be a quoted list like '(= X 1), where X is the element in question. F will be
processed by <a href="util-symbol.html#macro-expand">macro-expand</a>.
VARS:
(LST nil (LISTP LST))
(F nil (LISTP F))
TESTS:
(EQUAL (REMOVE '(1 2 3) '(> X 1)) '(1))
(EQUAL (REMOVE '(1 2 3) '(< X 1)) '(1 2 3))</pre>
</section>
<section>
<h3 id="remove!" class="funcsig">(<span class="funcname">remove!</span> lst-sym f)</h3>
<pre class="fulldoc">Updates list at LST-STM in place using <a href="util-list.html#remove">remove</a>
VARS:
(LST-SYM SYM (SYM-LST-P LST-SYM))
(F nil (LISTP F))
TESTS:
(EQUAL ((LAMBDA (LST) (REMOVE! 'LST '(> X 1)) LST) '(1 2 3)) '(1))
(EQUAL ((LAMBDA (LST) (REMOVE! 'LST '(< X 1)) LST) '(1 2 3)) '(1 2 3))</pre>
</section>
<section>
<h3 id="remove-last" class="funcsig">(<span class="funcname">remove-last</span> lst)</h3>
<pre class="fulldoc">Returns LST without its last element
VARS:
(LST nil (LISTP LST))
TESTS:
(EQUAL (REMOVE-LAST '(1 2 3)) '(1 2))</pre>
</section>
<section>
<h3 id="remove-nth" class="funcsig">(<span class="funcname">remove-nth</span> n lst)</h3>
<pre class="fulldoc">Returns LST with Nth item removed
VARS:
(N INT)
(LST nil (LISTP LST))
TESTS:
(EQUAL (REMOVE-NTH 1 '(1 2 3)) '(1 3))</pre>
</section>
<section>
<h3 id="rpt-lst" class="funcsig">(<span class="funcname">rpt-lst</span> n ele)</h3>
<pre class="fulldoc">Returns a list consisting of ELE repeated N times
Duplicate (args reversed) of <a href="util-list.html#lst*">lst*</a>*.
VARS:
(N INT)
TESTS:
(EQUAL (RPT-LST 3 'Q) '(Q Q Q))</pre>
</section>
<section>
<h3 id="safe-nth" class="funcsig">(<span class="funcname">safe-nth</span> n lst)</h3>
<pre class="fulldoc">Wrapper for nth that returns nil when LST is nil
Built-in nth errors in that case.
VARS:
(N INT)
(LST nil (LISTP LST))
TESTS:
(= (SAFE-NTH 1 '(1 2)) 2)
(= (SAFE-NTH 1 nil) nil)</pre>
</section>
<section>
<h3 id="slice" class="funcsig">(<span class="funcname">slice</span> lst start len)</h3>
<pre class="fulldoc">Return a slice from LST of length LEN starting at START
VARS:
(LST nil (LISTP LST))
(START INT)
(LEN INT)
TESTS:
(EQUAL (SLICE '(1 2 3 4) 0 4) '(1 2 3 4))
(EQUAL (SLICE '(1 2 3 4) 1 2) '(2 3))</pre>
</section>
<section>
<h3 id="sort!" class="funcsig">(<span class="funcname">sort!</span> lst-sym f)</h3>
<pre class="fulldoc">Sorts LST in place using vl-sort
VARS:
(LST-SYM SYM (SYM-LST-P LST-SYM))
(F nil (FUNCTION-OR-LAMBDA-P F))
TESTS:
(EQUAL ((LAMBDA (LST) (SORT! 'LST '<) LST) '(2 1 3)) '(1 2 3))</pre>
</section>
<section>
<h3 id="split-list" class="funcsig">(<span class="funcname">split-list</span> lst f true-lst-sym false-lst-sym)</h3>
<pre class="fulldoc">Puts items for which F returns nil in FALSE-LST and the rest in TRUE-LST
See <a href="util-list.html#remove">remove</a> and <a href="util-list.html#filter">filter</a> for how to format F.
VARS:
(LST nil (LISTP LST))
(TRUE-LST-SYM SYM (SYM-LST-P TRUE-LST-SYM))
(FALSE-LST-SYM SYM (SYM-LST-P FALSE-LST-SYM))</pre>
</section>
<section>
<h3 id="split-list-n" class="funcsig">(<span class="funcname">split-list-n</span> lst n first-lst-sym second-lst-sym)</h3>
<pre class="fulldoc">Splits LST on index N, putting first half in FIRST-LST and the rest in SECOND-LST
VARS:
(LST nil (LISTP LST))
(N INT)
(FIRST-LST-SYM SYM (SYM-LST-P FIRST-LST-SYM))
(SECOND-LST-SYM SYM (SYM-LST-P SECOND-LST-SYM))</pre>
</section>
<section>
<h3 id="strip-nil" class="funcsig">(<span class="funcname">strip-nil</span> lst)</h3>
<pre class="fulldoc">Returns LST with nil elements removed
VARS:
(LST nil (LISTP LST))
TESTS:
(EQUAL (STRIP-NIL '(1 nil 2)) '(1 2))
(EQUAL (STRIP-NIL '(1 2 3)) '(1 2 3))</pre>
</section>
<section>
<h3 id="subst!" class="funcsig">(<span class="funcname">subst!</span> new old lst-sym)</h3>
<pre class="fulldoc">Updates list at LST-SYM in place using subst
VARS:
(LST-SYM SYM (SYM-LST-P LST-SYM))
TESTS:
(EQUAL ((LAMBDA (LST) (SUBST! 'A 1 'LST) LST) '(1)) '(A))
(EQUAL ((LAMBDA (LST) (SUBST! 'A 1 'LST) LST) '(2)) '(2))</pre>
</section>
<section>
<h3 id="take" class="funcsig">(<span class="funcname">take</span> n lst)</h3>
<pre class="fulldoc">Returns the first N elements of LST
VARS:
(N INT)
(LST nil (LISTP LST))
TESTS:
(EQUAL (TAKE 1 '(1 2)) '(1))
(EQUAL (TAKE 2 '(1)) '(1))
(= (TAKE 1 nil) nil)</pre>
</section>
<section>
<h3 id="take!" class="funcsig">(<span class="funcname">take!</span> n take-lst-sym)</h3>
<pre class="fulldoc">Returns the first N elements from TAKE-LST-SYM, removing them
VARS:
(N (INT))
(TAKE-LST-SYM SYM (LISTP (VL-SYMBOL-VALUE TAKE-LST-SYM)))
TESTS:
(EQUAL ((LAMBDA (LST) (TAKE! 1 'LST)) '(1 2)) '(1))
(EQUAL ((LAMBDA (LST) (TAKE! 1 'LST) LST) '(1 2)) '(2))</pre>
</section>
<section>
<h3 id="take-while" class="funcsig">(<span class="funcname">take-while</span> predicate lst)</h3>
<pre class="fulldoc">Returns the first elements of LST for which PREDICATE is true
PREDICATE should be a function that takes one element.
VARS:
(PREDICATE (LIST SYM) (IF (LISTP PREDICATE) (= (CAR PREDICATE) 'LAMBDA) (FUNCTION-P (VL-SYMBOL-VALUE PREDICATE))))
(LST (LIST nil) (LISTP LST))
TESTS:
(EQUAL (TAKE-WHILE 'NUMBERP '(1 2 nil 3)) '(1 2))</pre>
</section>
<section>
<h3 id="test-for" class="funcsig">(<span class="funcname">test-for</span> test-type lst)</h3>
<pre class="fulldoc">Returns T if all elements of LST are of type TEST-TYPE
VARS:
(TEST-TYPE SYM)
(LST nil (LISTP LST))
TESTS:
(TEST-FOR 'INT '(1 2 3))
(TEST-FOR 'SYM '(A B C))
(NOT (TEST-FOR 'INT '(1 2 3.0)))</pre>
</section>
<section>
<h3 id="uniquify" class="funcsig">(<span class="funcname">uniquify</span> lst)</h3>
<pre class="fulldoc">Returns LST without duplicate items
VARS:
(LST nil (LISTP LST))
TESTS:
'(EQUAL (UNIQUIFY '(1 2 3 1 2)) '(1 2 3))</pre>
</section>
<section>
<h3 id="zip" class="funcsig">(<span class="funcname">zip</span> l1 l2)</h3>
<pre class="fulldoc">Returns lists L1 and L2 interleaved
VARS:
(L1 LIST (>= (LENGTH L1) (LENGTH L2)))
(L2 (LIST nil))
TESTS:
(EQUAL (ZIP '(1 2) '(A B)) '(1 A 2 B))
(EQUAL (ZIP '(1 2 3) '(A)) '(1 A 2 3))</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,146 @@
<!doctype html>
<html>
<head>
<title>util/object/3D.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/object/3D.lsp <a href="../../util/object/3D.lsp">[src]</a></h2>
<pre class="fulldoc">Functions related to creation of 3D objects</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-object-3D.html#closed-pline-p">(<span class="funcname">closed-pline-p</span> plineobj)</a></td>
<td>Returns T if passed PLINEOBJ is a closed polyline</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-3D.html#draw-fillet-3d">(<span class="funcname">draw-fillet-3d</span> container def-lst)</a></td>
<td>Creates a series of filleted plines from a definition list</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-3D.html#draw-pipe-3d">(<span class="funcname">draw-pipe-3d</span> path-lst rad)</a></td>
<td>Create a series of extruded circles along list of path objects</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-3D.html#path-p">(<span class="funcname">path-p</span> obj)</a></td>
<td>Returns T if passed OBJ is a valid path</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-3D.html#plineobj->faceobj">(<span class="funcname">plineobj-&gt;faceobj</span> plineobj dpt delete)</a></td>
<td>creates 3D face from an open pline input of exactly two vertices</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-3D.html#plineobj->regionobj">(<span class="funcname">plineobj-&gt;regionobj</span> plineobj delete)</a></td>
<td>Creates a region from passed polyline object</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-3D.html#plineobj->solidobj">(<span class="funcname">plineobj-&gt;solidobj</span> plineobj ext-param delete)</a></td>
<td>Creates extruded 3D solid from passed polyline object</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-3D.html#put-circle-normal-to-path">(<span class="funcname">put-circle-normal-to-path</span> obj path)</a></td>
<td>Move a passed circle object normal to a passed path object</td>
</tr>
</tbody>
</table>
<section>
<h3 id="closed-pline-p" class="funcsig">(<span class="funcname">closed-pline-p</span> plineobj)</h3>
<pre class="fulldoc">Returns T if passed PLINEOBJ is a closed polyline
VARS:
(PLINEOBJ VLA-OBJECT)</pre>
</section>
<section>
<h3 id="draw-fillet-3d" class="funcsig">(<span class="funcname">draw-fillet-3d</span> container def-lst)</h3>
<pre class="fulldoc">Creates a series of filleted plines from a definition list
DEF-LST is the return from <a href="util-geometry.html#safe-calc-fillet-3d">safe-calc-fillet-3d</a>
VARS:
(CONTAINER VLA-OBJECT)
(DEF-LST LIST (VL-EVERY '(LAMBDA (LST) (VL-EVERY 'POINT-P (MAPCAR 'REMOVE-LAST LST))) (MAPCAR 'CAR DEF-LST)) (VL-EVERY '(LAMBDA (LST) (VL-EVERY 'NUMBERP (MAPCAR 'LAST LST))) (MAPCAR 'CAR DEF-LST)) (VL-EVERY 'POINT-P (MAPCAR 'LAST DEF-LST)))</pre>
</section>
<section>
<h3 id="draw-pipe-3d" class="funcsig">(<span class="funcname">draw-pipe-3d</span> path-lst rad)</h3>
<pre class="fulldoc">Create a series of extruded circles along list of path objects
VARS:
(PATH-LST nil (VL-EVERY '(LAMBDA (OBJ) (= (TYPE OBJ) 'VLA-OBJECT)) PATH-LST))</pre>
</section>
<section>
<h3 id="path-p" class="funcsig">(<span class="funcname">path-p</span> obj)</h3>
<pre class="fulldoc">Returns T if passed OBJ is a valid path
VARS:
(OBJ VLA-OBJECT)</pre>
</section>
<section>
<h3 id="plineobj->faceobj" class="funcsig">(<span class="funcname">plineobj-&gt;faceobj</span> plineobj dpt delete)</h3>
<pre class="fulldoc">creates 3D face from an open pline input of exactly two vertices
VARS:
(PLINEOBJ VLA-OBJECT (= "AcDbPolyline" (vla-get-ObjectName PLINEOBJ)) ((LAMBDA (LST) (= 2 (LENGTH (SAFEARRAY->LIST LST)))) (variant-value (vla-get-Coordinates PLINEOBJ))))</pre>
</section>
<section>
<h3 id="plineobj->regionobj" class="funcsig">(<span class="funcname">plineobj-&gt;regionobj</span> plineobj delete)</h3>
<pre class="fulldoc">Creates a region from passed polyline object
PLINEOBJ must be a VLA-OBJ which is closed
PLINEOBJ will be deleted if DELETE is non-nil
VARS:
(PLINEOBJ VLA-OBJECT (CLOSED-PLINE-P PLINEOBJ))</pre>
</section>
<section>
<h3 id="plineobj->solidobj" class="funcsig">(<span class="funcname">plineobj-&gt;solidobj</span> plineobj ext-param delete)</h3>
<pre class="fulldoc">Creates extruded 3D solid from passed polyline object
PLINEOBJ must be a VLA-OBJ which is closed
EXT-PARAM must be a number for depth or a VLA-OBJECT for path
PLINEOBJ will be deleted if DELETE is non-nil
VARS:
(PLINEOBJ VLA-OBJECT (CLOSED-PLINE-P PLINEOBJ))
(EXT-PARAM nil (OR (NUMBERP EXT-PARAM) (PATH-P EXT-PARAM)))</pre>
</section>
<section>
<h3 id="put-circle-normal-to-path" class="funcsig">(<span class="funcname">put-circle-normal-to-path</span> obj path)</h3>
<pre class="fulldoc">Move a passed circle object normal to a passed path object
VARS:
(OBJ VLA-OBJECT (= "AcDbCircle" (vla-get-ObjectName OBJ)))
(PATH VLA-OBJECT (PATH-P PATH))</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,184 @@
<!doctype html>
<html>
<head>
<title>util/object/block.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/object/block.lsp <a href="../../util/object/block.lsp">[src]</a></h2>
<pre class="fulldoc">Block-handling functions</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-object-block.html#blkref-p">(<span class="funcname">blkref-p</span> obj)</a></td>
<td>Returns T if OBJ is a block reference</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-block.html#block-containing">(<span class="funcname">block-containing</span> sourceobj)</a></td>
<td>Searches for the block definition that contains SOURCEOBJ</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-block.html#blockdef">(<span class="funcname">blockdef</span> ename)</a></td>
<td>Returns the block definition for insert with ENAME</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-block.html#blockref2def">(<span class="funcname">blockref2def</span> blockref)</a></td>
<td>Returns the block definition for insert BLOCKREF</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-block.html#define-block">(<span class="funcname">define-block</span> name plines)</a></td>
<td>Returns a block definition named NAME consisting of closed polylines</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-block.html#define-block-maybe">(<span class="funcname">define-block-maybe</span> name plines)</a></td>
<td>Calls <a href="util-object-block.html#define-block">define-block</a> only if no block with NAME exists yet</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-block.html#insert-block">(<span class="funcname">insert-block</span> container ins block)</a></td>
<td>Wrapper for vla-InsertBlock</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-block.html#insert-lib-block">(<span class="funcname">insert-lib-block</span> container ins name scale)</a></td>
<td>Inserts a block from custom block library</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-block.html#move-to-bottom">(<span class="funcname">move-to-bottom</span> obj container)</a></td>
<td>Moves OBJ to bottom of draw order in CONTAINER</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-block.html#p-blk">(<span class="funcname">p-blk</span> str)</a></td>
<td>Returns a new empty pseudonymous block</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-block.html#p-blk-name">(<span class="funcname">p-blk-name</span> str)</a></td>
<td>Returns a unique pseudonymous block name</td>
</tr>
</tbody>
</table>
<section>
<h3 id="blkref-p" class="funcsig">(<span class="funcname">blkref-p</span> obj)</h3>
<pre class="fulldoc">Returns T if OBJ is a block reference
VARS:
(OBJ VLA-OBJECT)</pre>
</section>
<section>
<h3 id="block-containing" class="funcsig">(<span class="funcname">block-containing</span> sourceobj)</h3>
<pre class="fulldoc">Searches for the block definition that contains SOURCEOBJ
Returns the block name if found, or nil.
VARS:
(SOURCEOBJ VLA-OBJECT)</pre>
</section>
<section>
<h3 id="blockdef" class="funcsig">(<span class="funcname">blockdef</span> ename)</h3>
<pre class="fulldoc">Returns the block definition for insert with ENAME
VARS:
(ENAME ENAME)</pre>
</section>
<section>
<h3 id="blockref2def" class="funcsig">(<span class="funcname">blockref2def</span> blockref)</h3>
<pre class="fulldoc">Returns the block definition for insert BLOCKREF
VARS:
(BLOCKREF VLA-OBJECT)</pre>
</section>
<section>
<h3 id="define-block" class="funcsig">(<span class="funcname">define-block</span> name plines)</h3>
<pre class="fulldoc">Returns a block definition named NAME consisting of closed polylines
PLINES is a list of closed polylines inserted at the origin.
VARS:
(NAME STR)
(PLINES nil (LISTP PLINES) (VL-EVERY 'PTS-LIST-P PLINES))</pre>
</section>
<section>
<h3 id="define-block-maybe" class="funcsig">(<span class="funcname">define-block-maybe</span> name plines)</h3>
<pre class="fulldoc">Calls <a href="util-object-block.html#define-block">define-block</a> only if no block with NAME exists yet
VARS:
(NAME STR)
(PLINES nil (LISTP PLINES) (VL-EVERY 'PTS-LIST-P PLINES))</pre>
</section>
<section>
<h3 id="insert-block" class="funcsig">(<span class="funcname">insert-block</span> container ins block)</h3>
<pre class="fulldoc">Wrapper for vla-InsertBlock
BLOCK may be the block name as a string or a block definition obj. INS may be a point list
or vlax point variant.
VARS:
(CONTAINER VLA-OBJECT)
(INS (LIST variant nil))
(BLOCK (STR VLA-OBJECT))</pre>
</section>
<section>
<h3 id="insert-lib-block" class="funcsig">(<span class="funcname">insert-lib-block</span> container ins name scale)</h3>
<pre class="fulldoc">Inserts a block from custom block library
NAME should be the filename of a drawing from the blocks/ folder without the extension.
VARS:
(CONTAINER VLA-OBJECT)
(INS (LIST safearray nil))
(NAME STR)
(SCALE nil (NUMBERP SCALE))</pre>
</section>
<section>
<h3 id="move-to-bottom" class="funcsig">(<span class="funcname">move-to-bottom</span> obj container)</h3>
<pre class="fulldoc">Moves OBJ to bottom of draw order in CONTAINER
VARS:
(OBJ VLA-OBJECT)
(CONTAINER VLA-OBJECT)</pre>
</section>
<section>
<h3 id="p-blk" class="funcsig">(<span class="funcname">p-blk</span> str)</h3>
<pre class="fulldoc">Returns a new empty pseudonymous block</pre>
</section>
<section>
<h3 id="p-blk-name" class="funcsig">(<span class="funcname">p-blk-name</span> str)</h3>
<pre class="fulldoc">Returns a unique pseudonymous block name</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,174 @@
<!doctype html>
<html>
<head>
<title>util/object/line.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/object/line.lsp <a href="../../util/object/line.lsp">[src]</a></h2>
<pre class="fulldoc">Polyline handling functions</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-object-line.html#add-polyline">(<span class="funcname">add-polyline</span> container ins vertices closed-p)</a></td>
<td>Adds a LWPolyline to CONTAINER at INS</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-line.html#add-rectangle">(<span class="funcname">add-rectangle</span> container width height ins)</a></td>
<td>Adds a rectangular LWPolyline to CONTAINER using <a href="util-object-line.html#add-polyline">add-polyline</a></td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-line.html#add-slot">(<span class="funcname">add-slot</span> container ins len dia)</a></td>
<td>Adds a slot polyline to CONTAINER</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-line.html#dualoffset&delete">(<span class="funcname">dualoffset&delete</span> pline dist bulge)</a></td>
<td>Convenience wrapper for <a href="util-object-line.html#offset&close">offset&close</a></td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-line.html#get-vlist">(<span class="funcname">get-vlist</span> pline)</a></td>
<td>Returns the list of vertices comprising PLINE in the form (x y bulge)</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-line.html#line-obj->vert-lst">(<span class="funcname">line-obj-&gt;vert-lst</span> line)</a></td>
<td>Returns the list of vertices comprising LINE</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-line.html#offset&close">(<span class="funcname">offset&close</span> pline dist dual bulge)</a></td>
<td>Offsets PLINE by DIST and joins both lines into a closed polygon</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-line.html#offset-polyline">(<span class="funcname">offset-polyline</span> pline dist)</a></td>
<td>Offsets PLINE by DIST and returns the resulting polyline</td>
</tr>
</tbody>
</table>
<section>
<h3 id="add-polyline" class="funcsig">(<span class="funcname">add-polyline</span> container ins vertices closed-p)</h3>
<pre class="fulldoc">Adds a LWPolyline to CONTAINER at INS
INS should be a 2D or 3D point. VERTICES should be a list of points. Z-coordinates in
VERTICES are treated as bulge values (all points are 2D). Points without Z-coordinates
have 0 added to the end.
If CLOSED-P is non-nil, set the Closed property to True.
VARS:
(CONTAINER VLA-OBJECT)
(INS LIST (POINT-P INS))
(VERTICES LIST (VL-EVERY 'POINT-P VERTICES))</pre>
</section>
<section>
<h3 id="add-rectangle" class="funcsig">(<span class="funcname">add-rectangle</span> container width height ins)</h3>
<pre class="fulldoc">Adds a rectangular LWPolyline to CONTAINER using <a href="util-object-line.html#add-polyline">add-polyline</a>
INS is at the bottom-left corner of the rectangle. To put rectangles in other quadrants,
supply negative values for WIDTH, HEIGHT, or both.
VARS:
(CONTAINER VLA-OBJECT)
(WIDTH nil (NUMBERP WIDTH))
(HEIGHT nil (NUMBERP WIDTH))
(INS LIST (POINT-P INS))</pre>
</section>
<section>
<h3 id="add-slot" class="funcsig">(<span class="funcname">add-slot</span> container ins len dia)</h3>
<pre class="fulldoc">Adds a slot polyline to CONTAINER
INS is in the middle of the centerline and the slot extends horizontally to the left and
right by half of LEN. LEN describes the extents of the hole, including radius, not just
the centerline.
VARS:
(CONTAINER VLA-OBJECT)
(INS LIST (POINT-P INS))
(LEN nil (NUMBERP LEN))
(DIA nil (NUMBERP DIA))</pre>
</section>
<section>
<h3 id="dualoffset&delete" class="funcsig">(<span class="funcname">dualoffset&delete</span> pline dist bulge)</h3>
<pre class="fulldoc">Convenience wrapper for <a href="util-object-line.html#offset&close">offset&close</a>
Supplies t for DUAL argument, deletes the original polyline, and returns the resulting
offset polyline.
VARS:
(PLINE VLA-OBJECT)
(DIST nil (NUMBERP DIST))</pre>
</section>
<section>
<h3 id="get-vlist" class="funcsig">(<span class="funcname">get-vlist</span> pline)</h3>
<pre class="fulldoc">Returns the list of vertices comprising PLINE in the form (x y bulge)
VARS:
(PLINE VLA-OBJECT)</pre>
</section>
<section>
<h3 id="line-obj->vert-lst" class="funcsig">(<span class="funcname">line-obj-&gt;vert-lst</span> line)</h3>
<pre class="fulldoc">Returns the list of vertices comprising LINE
LINE can be a Line, Polyline or 3DPolyline
VARS:
(LINE VLA-OBJECT (MEMBER (vla-get-ObjectName LINE) '("AcDbLine" "AcDbPolyline" "AcDb3dPolyline")))</pre>
</section>
<section>
<h3 id="offset&close" class="funcsig">(<span class="funcname">offset&close</span> pline dist dual bulge)</h3>
<pre class="fulldoc">Offsets PLINE by DIST and joins both lines into a closed polygon
DIST being negative means to offset in the negative direction. If DUAL is non-nil, offset
in both directions and deletes the original line.
If BULGE is non-nil, either or both of the closing lines will be bulged out to a
semicircle. If BULGE is a non-nil scalar, both ends will be bulged. BULGE may also be a
2-item list, with the boolean value of the car indicating whether to bulge the end at the
head of the original line, and the cadr meaning the tail end.
VARS:
(PLINE VLA-OBJECT)
(DIST nil (NUMBERP DIST))</pre>
</section>
<section>
<h3 id="offset-polyline" class="funcsig">(<span class="funcname">offset-polyline</span> pline dist)</h3>
<pre class="fulldoc">Offsets PLINE by DIST and returns the resulting polyline
Essentially a wrapper for VLA-Offset that cracks the acorn and extracts the delicious
offset polyline hidden within a hard outer shell of safearray.
VARS:
(PLINE VLA-OBJECT)
(DIST nil (NUMBERP DIST))</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,261 @@
<!doctype html>
<html>
<head>
<title>util/object/table.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/object/table.lsp <a href="../../util/object/table.lsp">[src]</a></h2>
<pre class="fulldoc">Table-handling functions
Some functions here have side effects such as modifying text in MLEADER callouts to match
the table. This is primarily fab-oriented but, with care, can be used for any table
operations.</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-object-table.html#get-table-by-title">(<span class="funcname">get-table-by-title</span> title)</a></td>
<td>Returns the table with matching TITLE</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-table.html#gsl-mat-table-set-row">(<span class="funcname">gsl-mat-table-set-row</span> gsl-mat-table row data)</a></td>
<td>Sets a ROW in a generic ship loose template table</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-table.html#hdw-table-set-row">(<span class="funcname">hdw-table-set-row</span> hdw-table row data)</a></td>
<td>Sets a ROW in HDW-TABLE</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-table.html#mat-table-add-data">(<span class="funcname">mat-table-add-data</span> before datas)</a></td>
<td>Adds bulk data to material table</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-table.html#mat-table-set-row">(<span class="funcname">mat-table-set-row</span> mat-table row data)</a></td>
<td>Sets a ROW in MAT-TABLE</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-table.html#mat-table-update-ea-wt">(<span class="funcname">mat-table-update-ea-wt</span> mat-table)</a></td>
<td>Updates the formula for "each weight" in MAT-TABLE to include any added rows</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-table.html#mat-table-update-marks">(<span class="funcname">mat-table-update-marks</span> +- n at)</a></td>
<td>Update fab mark numbers in material table and callouts</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-table.html#parse-table">(<span class="funcname">parse-table</span> table)</a></td>
<td>Returns the data in TABLE as a nested list of strings</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-table.html#table-divide-qty">(<span class="funcname">table-divide-qty</span> table qty-col n)</a></td>
<td>Divides every quantity in TABLE by N</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-table.html#table-get-row">(<span class="funcname">table-get-row</span> table)</a></td>
<td>Prompts user for a point and checks if it is inside a row in TABLE</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-table.html#table-insert-rows">(<span class="funcname">table-insert-rows</span> table nrows before)</a></td>
<td>Inserts NROWS rows in a fab drawing TABLE before row BEFORE</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-table.html#table-multiply-qty">(<span class="funcname">table-multiply-qty</span> table qty-col n)</a></td>
<td>Multiplies every quantity in TABLE by N</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-table.html#table-p">(<span class="funcname">table-p</span> obj)</a></td>
<td>Returns T if OBJ is a table</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-table.html#table-row-extents">(<span class="funcname">table-row-extents</span> table row)</a></td>
<td>Returns the extents of a row in TABLE</td>
</tr>
</tbody>
</table>
<section>
<h3 id="get-table-by-title" class="funcsig">(<span class="funcname">get-table-by-title</span> title)</h3>
<pre class="fulldoc">Returns the table with matching TITLE
TITLE is matched against cell A1 of all tables in the database using wcmatch, so any
wildcards wcmatch accepts will work here.
VARS:
(TITLE STR)</pre>
</section>
<section>
<h3 id="gsl-mat-table-set-row" class="funcsig">(<span class="funcname">gsl-mat-table-set-row</span> gsl-mat-table row data)</h3>
<pre class="fulldoc">Sets a ROW in a generic ship loose template table
ROW is a row index and DATA is a data list whose keys correspond to the table columns.
VARS:
(GSL-MAT-TABLE VLA-OBJECT (TABLE-P GSL-MAT-TABLE))
(ROW INT (>= ROW 0))
(DATA nil (DATA-LIST-P DATA))</pre>
</section>
<section>
<h3 id="hdw-table-set-row" class="funcsig">(<span class="funcname">hdw-table-set-row</span> hdw-table row data)</h3>
<pre class="fulldoc">Sets a ROW in HDW-TABLE
ROW is a row index and DATA is a data list whose keys correspond to the table columns.
VARS:
(HDW-TABLE VLA-OBJECT (TABLE-P HDW-TABLE))
(ROW INT (>= ROW 0))
(DATA nil (DATA-LIST-P DATA))</pre>
</section>
<section>
<h3 id="mat-table-add-data" class="funcsig">(<span class="funcname">mat-table-add-data</span> before datas)</h3>
<pre class="fulldoc">Adds bulk data to material table
BEFORE is the row to add data at. The special symbol 'end means the last row. DATAS is a
list of data lists representing row data.
VARS:
(BEFORE nil (OR (= BEFORE 'END) (= (TYPE BEFORE) 'INT)))
(DATAS LIST (VL-EVERY 'DATA-LIST-P DATAS))</pre>
</section>
<section>
<h3 id="mat-table-set-row" class="funcsig">(<span class="funcname">mat-table-set-row</span> mat-table row data)</h3>
<pre class="fulldoc">Sets a ROW in MAT-TABLE
ROW is a row index and DATA is a data list whose keys correspond to the table columns.
VARS:
(MAT-TABLE VLA-OBJECT (TABLE-P MAT-TABLE))
(ROW INT (>= ROW 0))
(DATA nil (DATA-LIST-P DATA))</pre>
</section>
<section>
<h3 id="mat-table-update-ea-wt" class="funcsig">(<span class="funcname">mat-table-update-ea-wt</span> mat-table)</h3>
<pre class="fulldoc">Updates the formula for "each weight" in MAT-TABLE to include any added rows
VARS:
(MAT-TABLE VLA-OBJECT (TABLE-P MAT-TABLE))</pre>
</section>
<section>
<h3 id="mat-table-update-marks" class="funcsig">(<span class="funcname">mat-table-update-marks</span> +- n at)</h3>
<pre class="fulldoc">Update fab mark numbers in material table and callouts
+- should be one of the built-in functions + or -, indicating whether the mark numbers
should increase or decrease. N is the number of places the number should jump. AT is the
first row in the material table containing mark numbers to change.
Expects to be called by a function with TABLE set to the material table.
VARS:
(+- SUBR (MEMBER +- (LIST + -)))
(N INT)
(AT INT)
(TABLE VLA-OBJECT (TABLE-P TABLE))</pre>
</section>
<section>
<h3 id="parse-table" class="funcsig">(<span class="funcname">parse-table</span> table)</h3>
<pre class="fulldoc">Returns the data in TABLE as a nested list of strings
Skips the first two rows (assumes they are headers) and any rows whose first column is
blank.
VARS:
(TABLE VLA-OBJECT (TABLE-P TABLE))</pre>
</section>
<section>
<h3 id="table-divide-qty" class="funcsig">(<span class="funcname">table-divide-qty</span> table qty-col n)</h3>
<pre class="fulldoc">Divides every quantity in TABLE by N
QTY-COL is the index of the quantities column.
VARS:
(TABLE VLA-OBJECT (TABLE-P TABLE))
(QTY-COL INT (>= QTY-COL 0))
(N INT)</pre>
</section>
<section>
<h3 id="table-get-row" class="funcsig">(<span class="funcname">table-get-row</span> table)</h3>
<pre class="fulldoc">Prompts user for a point and checks if it is inside a row in TABLE
If so, it returns the index of that row.
VARS:
(TABLE VLA-OBJECT (TABLE-P TABLE))</pre>
</section>
<section>
<h3 id="table-insert-rows" class="funcsig">(<span class="funcname">table-insert-rows</span> table nrows before)</h3>
<pre class="fulldoc">Inserts NROWS rows in a fab drawing TABLE before row BEFORE
If there are other fab tables below this one, move them down to accomodate.
VARS:
(TABLE VLA-OBJECT (TABLE-P TABLE))
(NROWS INT)
(BEFORE INT)</pre>
</section>
<section>
<h3 id="table-multiply-qty" class="funcsig">(<span class="funcname">table-multiply-qty</span> table qty-col n)</h3>
<pre class="fulldoc">Multiplies every quantity in TABLE by N
QTY-COL is the index of the quantities column.
VARS:
(TABLE VLA-OBJECT (TABLE-P TABLE))
(QTY-COL INT (>= QTY-COL 0))
(N INT)</pre>
</section>
<section>
<h3 id="table-p" class="funcsig">(<span class="funcname">table-p</span> obj)</h3>
<pre class="fulldoc">Returns T if OBJ is a table</pre>
</section>
<section>
<h3 id="table-row-extents" class="funcsig">(<span class="funcname">table-row-extents</span> table row)</h3>
<pre class="fulldoc">Returns the extents of a row in TABLE
The return value is a 2-item list in the form '(top-left-pt bot-right-pt).
VARS:
(TABLE VLA-OBJECT (TABLE-P TABLE))
(ROW INT)</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,102 @@
<!doctype html>
<html>
<head>
<title>util/object/text.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/object/text.lsp <a href="../../util/object/text.lsp">[src]</a></h2>
<pre class="fulldoc">Functions pertaining to text, mtext, leader, and mleader objects</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-object-text.html#add-mleader">(<span class="funcname">add-mleader</span> container ins points text)</a></td>
<td>Wrapper for vla-AddMLeader</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-text.html#add-mtext">(<span class="funcname">add-mtext</span> container ins text width attachmentpoint)</a></td>
<td>Wrapper for vla-AddMText</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-text.html#add-text">(<span class="funcname">add-text</span> container ins text height width alignment)</a></td>
<td>Wrapper for vla-AddMText</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object-text.html#vla-replace-string">(<span class="funcname">vla-replace-string</span> block from to)</a></td>
<td>Replaces FROM with TO in all text, mtext, and mleaders in BLOCK</td>
</tr>
</tbody>
</table>
<section>
<h3 id="add-mleader" class="funcsig">(<span class="funcname">add-mleader</span> container ins points text)</h3>
<pre class="fulldoc">Wrapper for vla-AddMLeader
POINTS is a list of leader points which are relative to INS
VARS:
(CONTAINER VLA-OBJECT)
(INS LIST (POINT-P LIST))
(POINTS LIST (VL-EVERY 'POINT-P POINTS))
(TEXT STR)</pre>
</section>
<section>
<h3 id="add-mtext" class="funcsig">(<span class="funcname">add-mtext</span> container ins text width attachmentpoint)</h3>
<pre class="fulldoc">Wrapper for vla-AddMText
ATTACHMENTPOINT should be a symbol like 'middlecenter or 'topleft.
VARS:
(CONTAINER VLA-OBJECT)
(INS LIST (POINT-P INS))
(TEXT STR)
(WIDTH nil (NUMBERP WIDTH))
(ATTACHMENTPOINT SYM)</pre>
</section>
<section>
<h3 id="add-text" class="funcsig">(<span class="funcname">add-text</span> container ins text height width alignment)</h3>
<pre class="fulldoc">Wrapper for vla-AddMText
ALIGNMENT should be a symbol like 'middlecenter or 'topleft.
VARS:
(CONTAINER VLA-OBJECT)
(INS LIST (POINT-P INS))
(TEXT STR)
(HEIGHT nil (NUMBERP HEIGHT))
(WIDTH nil (NUMBERP WIDTH))
(ALIGNMENT SYM)</pre>
</section>
<section>
<h3 id="vla-replace-string" class="funcsig">(<span class="funcname">vla-replace-string</span> block from to)</h3>
<pre class="fulldoc">Replaces FROM with TO in all text, mtext, and mleaders in BLOCK
VARS:
(BLOCK VLA-OBJECT)
(FROM STR)
(TO STR)</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,138 @@
<!doctype html>
<html>
<head>
<title>util/object.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/object.lsp <a href="../../util/object.lsp">[src]</a></h2>
<pre class="fulldoc">VLA Object-handling functions</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-object.html#add-calcd-objs">(<span class="funcname">add-calcd-objs</span> container extrude-p defs)</a></td>
<td>Processes calculated objects for 2D and 3D blocks</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object.html#add-calcd-pipe">(<span class="funcname">add-calcd-pipe</span> container extrude-p def)</a></td>
<td>Adds a calculated pipe to CONTAINER</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object.html#add-calcd-poly">(<span class="funcname">add-calcd-poly</span> container extrude-p def)</a></td>
<td>Adds a calculated polyline to CONTAINER</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object.html#mirror&delete">(<span class="funcname">mirror&delete</span> obj pt axis)</a></td>
<td>Mirrors OBJ over AXIS at PT, deletes the original, and returns the copy</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object.html#vla-mirror-x">(<span class="funcname">vla-mirror-x</span> obj)</a></td>
<td>Mirrors VLA-object OBJ over x-axis using a transformation matrix</td>
</tr>
<tr>
<td class="funcsig"><a href="util-object.html#vla-mirror-y">(<span class="funcname">vla-mirror-y</span> obj)</a></td>
<td>Mirrors VLA-object OBJ over Y-axis using a transformation matrix</td>
</tr>
</tbody>
</table>
<section>
<h3 id="add-calcd-objs" class="funcsig">(<span class="funcname">add-calcd-objs</span> container extrude-p defs)</h3>
<pre class="fulldoc">Processes calculated objects for 2D and 3D blocks
CONTAINER should be the target block definition or model space. If EXTRUDE-P is non-nil,
creates 3D solids. Otherwise, creates a 2D wireframe. Additionally, when EXTRUDE-P is
non-nil, the object will be translated into the perspective used in 3D drawings.
DEFS is a list of calculated objects. Each is a list whose car is a data list of object
properties and whose cdr is the particular object definition. The value of the type key in
the properties determines which handling function is called. The naming scheme for
handling functions is 'add-calcd-' followed by the type value which triggers it.
Available handling functions:
- <a href="util-object.html#add-calcd-poly">add-calcd-poly</a>
- <a href="util-object.html#add-calcd-pipe">add-calcd-pipe</a>
Common properties:
- ins: insert point (all pts in definition displaced relative to ins)
- layer: layer to put obj on
- extrude: extrusion distance
VARS:
(CONTAINER VLA-OBJECT)
(DEFS nil (LISTP DEFS))</pre>
</section>
<section>
<h3 id="add-calcd-pipe" class="funcsig">(<span class="funcname">add-calcd-pipe</span> container extrude-p def)</h3>
<pre class="fulldoc">Adds a calculated pipe to CONTAINER
Pipe objects represent bent pipe handrails. They are represented as open polylines. This
function fillets the points list and either offsets or extrudes the pipe.
Addition properties available to pipes:
- radius: fillet radius
- bulge: used as 4th argument to <a href="util-object-line.html#offset&close">offset&close</a></pre>
</section>
<section>
<h3 id="add-calcd-poly" class="funcsig">(<span class="funcname">add-calcd-poly</span> container extrude-p def)</h3>
<pre class="fulldoc">Adds a calculated polyline to CONTAINER
CONTAINER should be the target block definition or model space. DEF should be a list whose
car is a data list of properties and whose cdr is a list of points representing a closed
polyline. If EXTRUDE-P is non-nil, the closed polyline is extruded by the value of the
"extrude" property.</pre>
</section>
<section>
<h3 id="mirror&delete" class="funcsig">(<span class="funcname">mirror&delete</span> obj pt axis)</h3>
<pre class="fulldoc">Mirrors OBJ over AXIS at PT, deletes the original, and returns the copy
OBJ should be a VLA-object. Axis should be one of the symbols 'x or 'y. PT should be a
point.
VARS:
(OBJ VLA-OBJECT)
(PT LIST (POINT-P PT))
(AXIS SYM (MEMBER AXIS '(X Y)))</pre>
</section>
<section>
<h3 id="vla-mirror-x" class="funcsig">(<span class="funcname">vla-mirror-x</span> obj)</h3>
<pre class="fulldoc">Mirrors VLA-object OBJ over x-axis using a transformation matrix
VARS:
(OBJ VLA-OBJECT)</pre>
</section>
<section>
<h3 id="vla-mirror-y" class="funcsig">(<span class="funcname">vla-mirror-y</span> obj)</h3>
<pre class="fulldoc">Mirrors VLA-object OBJ over Y-axis using a transformation matrix
VARS:
(OBJ VLA-OBJECT)</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,185 @@
<!doctype html>
<html>
<head>
<title>util/selection.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/selection.lsp <a href="../../util/selection.lsp">[src]</a></h2>
<pre class="fulldoc">Functions related to user selection</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-selection.html#dxf-filter">(<span class="funcname">dxf-filter</span> defs)</a></td>
<td>Translates S-Expressions into DXF codes for passing to ssget</td>
</tr>
<tr>
<td class="funcsig"><a href="util-selection.html#get-ins-and-delete">(<span class="funcname">get-ins-and-delete</span> ename)</a></td>
<td>Deletes the entity named ENAME and returns its former insertion point</td>
</tr>
<tr>
<td class="funcsig"><a href="util-selection.html#get-sset">(<span class="funcname">get-sset</span> app-ids-str)</a></td>
<td>Gets a selection from the user of objects with given XDATA app-ID</td>
</tr>
<tr>
<td class="funcsig"><a href="util-selection.html#get-stairland">(<span class="funcname">get-stairland</span> selection sort-p)</a></td>
<td>Gets a selection of stairs and landings from the user</td>
</tr>
<tr>
<td class="funcsig"><a href="util-selection.html#getdint">(<span class="funcname">getdint</span> def-sym prompt-str)</a></td>
<td>Wrapper for getint that uses default values and remembers the last choice</td>
</tr>
<tr>
<td class="funcsig"><a href="util-selection.html#getdkword">(<span class="funcname">getdkword</span> ig-args def-sym prompt-str)</a></td>
<td>Wrapper for initget/getkword that uses default values and remembers the last choice</td>
</tr>
<tr>
<td class="funcsig"><a href="util-selection.html#ss->lst">(<span class="funcname">ss-&gt;lst</span> ss)</a></td>
<td>Returns selection set SS as a list of enames</td>
</tr>
<tr>
<td class="funcsig"><a href="util-selection.html#ss-and">(<span class="funcname">ss-and</span> group-codes)</a></td>
<td>Returns a list of DXF codes wrapped in a DXF AND</td>
</tr>
<tr>
<td class="funcsig"><a href="util-selection.html#ss-or">(<span class="funcname">ss-or</span> group-codes)</a></td>
<td>Returns a list of DXF codes wrapped in a DXF OR</td>
</tr>
<tr>
<td class="funcsig"><a href="util-selection.html#vlasel">(<span class="funcname">vlasel</span>)</a></td>
<td>Alias for (ename>vlobj (car (entsel)))</td>
</tr>
</tbody>
</table>
<section>
<h3 id="dxf-filter" class="funcsig">(<span class="funcname">dxf-filter</span> defs)</h3>
<pre class="fulldoc">Translates S-Expressions into DXF codes for passing to ssget
DEFS should be a list of group code S-Expressions. Note: <a href="util-symbol.html#macro-expand">macro-expand</a> is called on DEFS
before use. See source for which codes are available for use.
VARS:
(DEFS nil (LISTP DEFS) (VL-EVERY 'LISTP DEFS))
TESTS:
(EQUAL (DXF-FILTER '((APPID "Land"))) '((-3 ("Land"))))
(EQUAL (DXF-FILTER '((AND (TYPE "CIRCLE") (LAYER "DIM")))) '((-4 . "<AND") (0 . "CIRCLE") (8 . "DIM") (-4 . "AND>")))</pre>
</section>
<section>
<h3 id="get-ins-and-delete" class="funcsig">(<span class="funcname">get-ins-and-delete</span> ename)</h3>
<pre class="fulldoc">Deletes the entity named ENAME and returns its former insertion point
VARS:
(ENAME ENAME)</pre>
</section>
<section>
<h3 id="get-sset" class="funcsig">(<span class="funcname">get-sset</span> app-ids-str)</h3>
<pre class="fulldoc">Gets a selection from the user of objects with given XDATA app-ID</pre>
</section>
<section>
<h3 id="get-stairland" class="funcsig">(<span class="funcname">get-stairland</span> selection sort-p)</h3>
<pre class="fulldoc">Gets a selection of stairs and landings from the user
Returns enames of selected objects. SELECTION should be one of the following symbols:
- 'stair
- 'land
- 'both
SORT-P being non-nil means to sort selection set using <a href="util-comparison.html#sort-stairland">sort-stairland</a>.
VARS:
(SELECTION SYM (MEMBER SELECTION '(STAIR LAND BOTH)))</pre>
</section>
<section>
<h3 id="getdint" class="funcsig">(<span class="funcname">getdint</span> def-sym prompt-str)</h3>
<pre class="fulldoc">Wrapper for getint that uses default values and remembers the last choice
See <a href="util-selection.html#getdkword">getdkword</a> for usage of DEF-SYM, PROMPT-STR, and MATCH-LAST-P.
VARS:
(DEF-SYM (SYM))
(PROMPT-STR (STR))</pre>
</section>
<section>
<h3 id="getdkword" class="funcsig">(<span class="funcname">getdkword</span> ig-args def-sym prompt-str)</h3>
<pre class="fulldoc">Wrapper for initget/getkword that uses default values and remembers the last choice
IG-ARGS is a 2-item list of args to initget. The first element should be an int and the
second should be the list of keywords as a string. DEF-SYM is a symbol pointing to the
variable that holds the default value. PROMPT-STR is the prompt string, to which a leading
newline and the bracketed prompt items are added automatically.
If the keyword list contains a variant of "matchlast" (case-insensitive, so the caller
can determine the matching key sequence), the user may enter that keyword to skip
remaining prompts. This works by checking the value of the MATCH-LAST-P variable, which
should be set local in the calling command if used.
VARS:
(IG-ARGS (LIST) (= (LENGTH IG-ARGS) 2) (= (TYPE (CAR IG-ARGS)) 'INT) (STRINGP (LAST IG-ARGS)))
(DEF-SYM (SYM))
(PROMPT-STR (STR))</pre>
</section>
<section>
<h3 id="ss->lst" class="funcsig">(<span class="funcname">ss-&gt;lst</span> ss)</h3>
<pre class="fulldoc">Returns selection set SS as a list of enames</pre>
</section>
<section>
<h3 id="ss-and" class="funcsig">(<span class="funcname">ss-and</span> group-codes)</h3>
<pre class="fulldoc">Returns a list of DXF codes wrapped in a DXF AND
Mainly for use in <a href="util-selection.html#dxf-filter">dxf-filter</a>.
VARS:
(GROUP-CODES nil (LISTP GROUP-CODES))</pre>
</section>
<section>
<h3 id="ss-or" class="funcsig">(<span class="funcname">ss-or</span> group-codes)</h3>
<pre class="fulldoc">Returns a list of DXF codes wrapped in a DXF OR
Mainly for use in <a href="util-selection.html#dxf-filter">dxf-filter</a>.
VARS:
(GROUP-CODES nil (LISTP GROUP-CODES))</pre>
</section>
<section>
<h3 id="vlasel" class="funcsig">(<span class="funcname">vlasel</span>)</h3>
<pre class="fulldoc">Alias for (ename>vlobj (car (entsel)))</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,265 @@
<!doctype html>
<html>
<head>
<title>util/string.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/string.lsp <a href="../../util/string.lsp">[src]</a></h2>
<pre class="fulldoc">String functions</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-string.html#begins-with">(<span class="funcname">begins-with</span> str pattern)</a></td>
<td>Returns nil unless STR begins with PATTERN</td>
</tr>
<tr>
<td class="funcsig"><a href="util-string.html#ends-with">(<span class="funcname">ends-with</span> str pattern)</a></td>
<td>Returns nil unless STR ends with PATTERN</td>
</tr>
<tr>
<td class="funcsig"><a href="util-string.html#escape-newlines">(<span class="funcname">escape-newlines</span> str)</a></td>
<td>Returns STR with newlines escaped</td>
</tr>
<tr>
<td class="funcsig"><a href="util-string.html#escape-quotes">(<span class="funcname">escape-quotes</span> str)</a></td>
<td>Returns STR with double-quotes escaped</td>
</tr>
<tr>
<td class="funcsig"><a href="util-string.html#first-line">(<span class="funcname">first-line</span> str)</a></td>
<td>Returns the first line of STR</td>
</tr>
<tr>
<td class="funcsig"><a href="util-string.html#lowercase">(<span class="funcname">lowercase</span> str)</a></td>
<td>Returns STR in lowercase</td>
</tr>
<tr>
<td class="funcsig"><a href="util-string.html#strbreak">(<span class="funcname">strbreak</span> str)</a></td>
<td>Returns a list of single character strings comprising STR</td>
</tr>
<tr>
<td class="funcsig"><a href="util-string.html#string-subst-all">(<span class="funcname">string-subst-all</span> new old str)</a></td>
<td>Uses vl-string-subst repeatedly to substitute all occurrences of OLD for NEW in STR</td>
</tr>
<tr>
<td class="funcsig"><a href="util-string.html#stringp">(<span class="funcname">stringp</span> x)</a></td>
<td>Returns nil if X is not string</td>
</tr>
<tr>
<td class="funcsig"><a href="util-string.html#strjoin">(<span class="funcname">strjoin</span> str-list delimiter)</a></td>
<td>Concatenates strings in STR-LIST with DELIMITER</td>
</tr>
<tr>
<td class="funcsig"><a href="util-string.html#strsplit">(<span class="funcname">strsplit</span> str delimiter)</a></td>
<td>Splits STR on a DELIMITER</td>
</tr>
<tr>
<td class="funcsig"><a href="util-string.html#uppercase">(<span class="funcname">uppercase</span> str)</a></td>
<td>Returns STR in uppercase</td>
</tr>
<tr>
<td class="funcsig"><a href="util-string.html#wcmatch-escape">(<span class="funcname">wcmatch-escape</span> str)</a></td>
<td>Escapes special characters in STR for use in wcmatch</td>
</tr>
</tbody>
</table>
<section>
<h3 id="begins-with" class="funcsig">(<span class="funcname">begins-with</span> str pattern)</h3>
<pre class="fulldoc">Returns nil unless STR begins with PATTERN
VARS:
(STR STR)
(PATTERN STR)
TESTS:
(BEGINS-WITH "hello" "h")
(BEGINS-WITH "hello" "")
(NOT (BEGINS-WITH "hello" "a"))</pre>
</section>
<section>
<h3 id="ends-with" class="funcsig">(<span class="funcname">ends-with</span> str pattern)</h3>
<pre class="fulldoc">Returns nil unless STR ends with PATTERN
VARS:
(STR STR)
(PATTERN STR)
TESTS:
(ENDS-WITH "hello" "o")
(ENDS-WITH "hello" "")
(NOT (ENDS-WITH "hello" "a"))</pre>
</section>
<section>
<h3 id="escape-newlines" class="funcsig">(<span class="funcname">escape-newlines</span> str)</h3>
<pre class="fulldoc">Returns STR with newlines escaped
VARS:
(STR STR)
TESTS:
(= (ESCAPE-NEWLINES "hi") "hi")
(= (ESCAPE-NEWLINES "item1
item2") "item1\nitem2")</pre>
</section>
<section>
<h3 id="escape-quotes" class="funcsig">(<span class="funcname">escape-quotes</span> str)</h3>
<pre class="fulldoc">Returns STR with double-quotes escaped
VARS:
(STR STR)</pre>
</section>
<section>
<h3 id="first-line" class="funcsig">(<span class="funcname">first-line</span> str)</h3>
<pre class="fulldoc">Returns the first line of STR
VARS:
(STR STR)
TESTS:
(= (FIRST-LINE "hi") "hi")
(= (FIRST-LINE "item1
item2") "item1")</pre>
</section>
<section>
<h3 id="lowercase" class="funcsig">(<span class="funcname">lowercase</span> str)</h3>
<pre class="fulldoc">Returns STR in lowercase
VARS:
(STR STR)
TESTS:
(= (LOWERCASE "hi") "hi")
(= (LOWERCASE "HI") "hi")</pre>
</section>
<section>
<h3 id="strbreak" class="funcsig">(<span class="funcname">strbreak</span> str)</h3>
<pre class="fulldoc">Returns a list of single character strings comprising STR
VARS:
(STR STR)
TESTS:
(EQUAL (STRBREAK "hi") '("h" "i"))
(= (STRBREAK "") nil)</pre>
</section>
<section>
<h3 id="string-subst-all" class="funcsig">(<span class="funcname">string-subst-all</span> new old str)</h3>
<pre class="fulldoc">Uses vl-string-subst repeatedly to substitute all occurrences of OLD for NEW in STR
VARS:
(NEW STR)
(OLD STR)
(STR STR)
TESTS:
(= (STRING-SUBST-ALL "hello" "hi" "hi hi") "hello hello")</pre>
</section>
<section>
<h3 id="stringp" class="funcsig">(<span class="funcname">stringp</span> x)</h3>
<pre class="fulldoc">Returns nil if X is not string
TESTS:
(STRINGP "hello")
(NOT (STRINGP 1))</pre>
</section>
<section>
<h3 id="strjoin" class="funcsig">(<span class="funcname">strjoin</span> str-list delimiter)</h3>
<pre class="fulldoc">Concatenates strings in STR-LIST with DELIMITER
Example:
(strjoin '("hello" "world") ", ")
returns the string "hello, world".
VARS:
(STR-LIST nil (LISTP STR-LIST) (VL-EVERY 'STRINGP STR-LIST))
(DELIMITER STR)
TESTS:
(= (STRJOIN '("key" "val") "=") "key=val")
(= (STRJOIN '("hello" "world") ", ") "hello, world")</pre>
</section>
<section>
<h3 id="strsplit" class="funcsig">(<span class="funcname">strsplit</span> str delimiter)</h3>
<pre class="fulldoc">Splits STR on a DELIMITER
Example:
(strsplit "hello, world" ", ")
returns the list ("hello" "world").
VARS:
(STR STR)
(DELIMITER STR)
TESTS:
(EQUAL (STRSPLIT "key=val" "=") '("key" "val"))
(EQUAL (STRSPLIT "hello, world" ", ") '("hello" "world"))</pre>
</section>
<section>
<h3 id="uppercase" class="funcsig">(<span class="funcname">uppercase</span> str)</h3>
<pre class="fulldoc">Returns STR in uppercase
VARS:
(STR STR)
TESTS:
(= (UPPERCASE "HI") "HI")
(= (UPPERCASE "hi") "HI")</pre>
</section>
<section>
<h3 id="wcmatch-escape" class="funcsig">(<span class="funcname">wcmatch-escape</span> str)</h3>
<pre class="fulldoc">Escapes special characters in STR for use in wcmatch
VARS:
(STR STR)
TESTS:
(= (WCMATCH-ESCAPE "what # is 1*1?") "what `# is 1`*1`?")
(= (WCMATCH-ESCAPE "backtick[`]@my-mail.com~") "backtick`[```]`@my`-mail`.com`~")</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,218 @@
<!doctype html>
<html>
<head>
<title>util/symbol.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/symbol.lsp <a href="../../util/symbol.lsp">[src]</a></h2>
<pre class="fulldoc">Symbol-handling functions</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-symbol.html##!">(<span class="funcname">#!</span> expr-lst)</a></td>
<td>Alias for <a href="util-symbol.html#macro-expand">macro-expand</a></td>
</tr>
<tr>
<td class="funcsig"><a href="util-symbol.html#chkargs">(<span class="funcname">chkargs</span> chkargs-func-name chkargs-args)</a></td>
<td>Validates arguments in the current function.</td>
</tr>
<tr>
<td class="funcsig"><a href="util-symbol.html#cornerkey">(<span class="funcname">cornerkey</span> key)</a></td>
<td>Returns the value of the symbol named [CORNER]_[KEY].</td>
</tr>
<tr>
<td class="funcsig"><a href="util-symbol.html#defined">(<span class="funcname">defined</span> x)</a></td>
<td>Returns nil if X is null.</td>
</tr>
<tr>
<td class="funcsig"><a href="util-symbol.html#fbkey">(<span class="funcname">fbkey</span> key)</a></td>
<td>Returns the value of the symbol named [FB]_[KEY].</td>
</tr>
<tr>
<td class="funcsig"><a href="util-symbol.html#lrkey">(<span class="funcname">lrkey</span> key)</a></td>
<td>Returns the value of the symbol named [LR]_[KEY].</td>
</tr>
<tr>
<td class="funcsig"><a href="util-symbol.html#macro-expand">(<span class="funcname">macro-expand</span> expr-lst)</a></td>
<td>Return quoted list EXPR-LST with certain elements evaluated.</td>
</tr>
<tr>
<td class="funcsig"><a href="util-symbol.html#propagate">(<span class="funcname">propagate</span> var-sym value)</a></td>
<td>Sets VAR-SYM to VALUE and calls vl-propagate</td>
</tr>
<tr>
<td class="funcsig"><a href="util-symbol.html#sidekey">(<span class="funcname">sidekey</span> key)</a></td>
<td>Returns the value of the symbol named [SIDE]-[KEY].</td>
</tr>
<tr>
<td class="funcsig"><a href="util-symbol.html#sidekey-rail">(<span class="funcname">sidekey-rail</span> key)</a></td>
<td>Returns the value of the symbol named [SIDE]-rail_[KEY].</td>
</tr>
<tr>
<td class="funcsig"><a href="util-symbol.html#swap!">(<span class="funcname">swap!</span> v1 v2)</a></td>
<td>Swaps two variables.</td>
</tr>
<tr>
<td class="funcsig"><a href="util-symbol.html#sym-lst-p">(<span class="funcname">sym-lst-p</span> sym)</a></td>
<td>Returns nil if SYM is not a symbol or if its value is not a list</td>
</tr>
<tr>
<td class="funcsig"><a href="util-symbol.html#symcat">(<span class="funcname">symcat</span> str-lst)</a></td>
<td>Concatenates strings in STR-LST and returns the value of the resulting symbol.</td>
</tr>
<tr>
<td class="funcsig"><a href="util-symbol.html#tbkey">(<span class="funcname">tbkey</span> key)</a></td>
<td>Returns the value of the symbol named [TB]_[KEY].</td>
</tr>
</tbody>
</table>
<section>
<h3 id="#!" class="funcsig">(<span class="funcname">#!</span> expr-lst)</h3>
<pre class="fulldoc">Alias for <a href="util-symbol.html#macro-expand">macro-expand</a></pre>
</section>
<section>
<h3 id="chkargs" class="funcsig">(<span class="funcname">chkargs</span> chkargs-func-name chkargs-args)</h3>
<pre class="fulldoc">Validates arguments in the current function.
See <a href="util-documentation.html#defun-r--process-declare-vars">defun-r--process-declare-vars</a> for a simplified way to invoke this function.
CHKARGS-FUNC-NAME should be the current function's name as a string. It is used for
reporting errors.
CHKARGS-ARGS should be an argument spec. Its length should be equal to the number of
arguments this function takes. At minimum, CHKARGS-ARGS should be a list of argument names
as symbols.
For each item in CHKARGS-ARGS:
[car] is the argument name as a symbol.
[cadr] is the expected type, which may be a symbol or a list of symbols
[caddr] is an optional list. Each item is a validator to be run against [car]. This may be
a symbol pointing to a function which takes one argument or a list to eval. If a validator
returns nil, throw an error.</pre>
</section>
<section>
<h3 id="cornerkey" class="funcsig">(<span class="funcname">cornerkey</span> key)</h3>
<pre class="fulldoc">Returns the value of the symbol named [CORNER]_[KEY].
VARS:
(CORNER STR)
(KEY STR)</pre>
</section>
<section>
<h3 id="defined" class="funcsig">(<span class="funcname">defined</span> x)</h3>
<pre class="fulldoc">Returns nil if X is null.</pre>
</section>
<section>
<h3 id="fbkey" class="funcsig">(<span class="funcname">fbkey</span> key)</h3>
<pre class="fulldoc">Returns the value of the symbol named [FB]_[KEY].
VARS:
(FB STR)
(KEY STR)</pre>
</section>
<section>
<h3 id="lrkey" class="funcsig">(<span class="funcname">lrkey</span> key)</h3>
<pre class="fulldoc">Returns the value of the symbol named [LR]_[KEY].
VARS:
(LR STR)
(KEY STR)</pre>
</section>
<section>
<h3 id="macro-expand" class="funcsig">(<span class="funcname">macro-expand</span> expr-lst)</h3>
<pre class="fulldoc">Return quoted list EXPR-LST with certain elements evaluated.
Macro-expand will evaluate elements of EXPR-LST which are prefaced by a #. Example:
(setq foo 4
bar 7)
(macro-expand '(+ 1 #foo #(+ bar foo 3)))
will return the list '(+ 1 4 14).</pre>
</section>
<section>
<h3 id="propagate" class="funcsig">(<span class="funcname">propagate</span> var-sym value)</h3>
<pre class="fulldoc">Sets VAR-SYM to VALUE and calls vl-propagate</pre>
</section>
<section>
<h3 id="sidekey" class="funcsig">(<span class="funcname">sidekey</span> key)</h3>
<pre class="fulldoc">Returns the value of the symbol named [SIDE]-[KEY].
VARS:
(SIDE STR)
(KEY STR)</pre>
</section>
<section>
<h3 id="sidekey-rail" class="funcsig">(<span class="funcname">sidekey-rail</span> key)</h3>
<pre class="fulldoc">Returns the value of the symbol named [SIDE]-rail_[KEY].
VARS:
(SIDE STR)
(KEY STR)</pre>
</section>
<section>
<h3 id="swap!" class="funcsig">(<span class="funcname">swap!</span> v1 v2)</h3>
<pre class="fulldoc">Swaps two variables.</pre>
</section>
<section>
<h3 id="sym-lst-p" class="funcsig">(<span class="funcname">sym-lst-p</span> sym)</h3>
<pre class="fulldoc">Returns nil if SYM is not a symbol or if its value is not a list</pre>
</section>
<section>
<h3 id="symcat" class="funcsig">(<span class="funcname">symcat</span> str-lst)</h3>
<pre class="fulldoc">Concatenates strings in STR-LST and returns the value of the resulting symbol.</pre>
</section>
<section>
<h3 id="tbkey" class="funcsig">(<span class="funcname">tbkey</span> key)</h3>
<pre class="fulldoc">Returns the value of the symbol named [TB]_[KEY].
VARS:
(TB STR)
(KEY STR)</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,83 @@
<!doctype html>
<html>
<head>
<title>util/test.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/test.lsp <a href="../../util/test.lsp">[src]</a></h2>
<pre class="fulldoc">Functions related to unit testing</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-test.html#defun-t">(<span class="funcname">defun-t</span> test-sym)</a></td>
<td>Registers function at TEST-SYM as a unit test.</td>
</tr>
<tr>
<td class="funcsig"><a href="util-test.html#test-build">(<span class="funcname">test-build</span>)</a></td>
<td>Bootstraps test build process and opens a new blank drawing to carry it out.</td>
</tr>
<tr>
<td class="funcsig"><a href="util-test.html#test-build-lsp">(<span class="funcname">test-build-lsp</span>)</a></td>
<td>Writes tests.lsp with all the test forms in *file-tests*</td>
</tr>
<tr>
<td class="funcsig"><a href="util-test.html#test-run">(<span class="funcname">test-run</span>)</a></td>
<td>Alias for (load "tests.lsp").</td>
</tr>
</tbody>
</table>
<section>
<h3 id="defun-t" class="funcsig">(<span class="funcname">defun-t</span> test-sym)</h3>
<pre class="fulldoc">Registers function at TEST-SYM as a unit test.
Test functions should:
- take no arguments
- have short (1-line) docstrings describing what they test
- return a list of assertion errors using <a href="util-error.html#assert-all">assert-all</a>
- be self contained
- NOT set any symbols in the global environment
Each test function will be wrapped with advice that reports failures by printing its name,
docstring, and errors (otherwise it returns nil). A call to the resulting function will be
saved to *file-tests* so <a href="util-test.html#test-build-lsp">test-build-lsp</a> can write them to tests.lsp. Then, TEST-SYM will
be set to nil to mark the original test function for garbage collection.</pre>
</section>
<section>
<h3 id="test-build" class="funcsig">(<span class="funcname">test-build</span>)</h3>
<pre class="fulldoc">Bootstraps test build process and opens a new blank drawing to carry it out.</pre>
</section>
<section>
<h3 id="test-build-lsp" class="funcsig">(<span class="funcname">test-build-lsp</span>)</h3>
<pre class="fulldoc">Writes tests.lsp with all the test forms in *file-tests*</pre>
</section>
<section>
<h3 id="test-run" class="funcsig">(<span class="funcname">test-run</span>)</h3>
<pre class="fulldoc">Alias for (load "tests.lsp").</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,74 @@
<!doctype html>
<html>
<head>
<title>util/time.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/time.lsp <a href="../../util/time.lsp">[src]</a></h2>
<pre class="fulldoc">Time- and date-related functions</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-time.html#!avg-time">(<span class="funcname">!avg-time</span> fun num)</a></td>
<td>Prints statistical results after calling <a href="util-time.html#!time">!time</a> NUM times</td>
</tr>
<tr>
<td class="funcsig"><a href="util-time.html#!time">(<span class="funcname">!time</span> func alert-p)</a></td>
<td>Prints the number of milliseconds it takes to run FUNC</td>
</tr>
<tr>
<td class="funcsig"><a href="util-time.html#curdate">(<span class="funcname">curdate</span>)</a></td>
<td>Returns the current date formatted as DD/MM/YYYY</td>
</tr>
</tbody>
</table>
<section>
<h3 id="!avg-time" class="funcsig">(<span class="funcname">!avg-time</span> fun num)</h3>
<pre class="fulldoc">Prints statistical results after calling <a href="util-time.html#!time">!time</a> NUM times
FUN is the first argument to <a href="util-time.html#!time">!time</a>. This function passes nil for ALERT-P.
VARS:
(FUN LIST (FUNCTION-P (EVAL (CAR FUN))))
(NUM INT)</pre>
</section>
<section>
<h3 id="!time" class="funcsig">(<span class="funcname">!time</span> func alert-p)</h3>
<pre class="fulldoc">Prints the number of milliseconds it takes to run FUNC
FUNC should be a lisp form to be eval'd. If ALERT-P is nil, prints to the command
line. Otherwise, displays an alert box with the result.
Example: (!time '(repeat 100 (read-xdata (entlast) "")) nil)
VARS:
(FUNC LIST (FUNCTION-P (EVAL (CAR FUNC))))</pre>
</section>
<section>
<h3 id="curdate" class="funcsig">(<span class="funcname">curdate</span>)</h3>
<pre class="fulldoc">Returns the current date formatted as DD/MM/YYYY</pre>
</section>
</div>
</body>
</html>

@ -0,0 +1,83 @@
<!doctype html>
<html>
<head>
<title>util/xdata.lsp | PSC Code Manual</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1><a href="./index.html">PSC Code Manual</a></h1>
<div id="content">
<h2>util/xdata.lsp <a href="../../util/xdata.lsp">[src]</a></h2>
<pre class="fulldoc">XDATA-handling functions</pre>
<table>
<thead>
<h3>Functions</h3>
<th>Function signature</th>
<th>Description</th>
</thead>
<tbody>
<tr>
<td class="funcsig"><a href="util-xdata.html#has-duplicate-keys-p">(<span class="funcname">has-duplicate-keys-p</span> data)</a></td>
<td>Returns T if any keys in data list DATA are repeated</td>
</tr>
<tr>
<td class="funcsig"><a href="util-xdata.html#subst-key">(<span class="funcname">subst-key</span> key new-val lst)</a></td>
<td>Returns data list LST with value of KEY replaced with NEW-VAL</td>
</tr>
<tr>
<td class="funcsig"><a href="util-xdata.html#subst-key!">(<span class="funcname">subst-key!</span> key new-val lst-sym)</a></td>
<td>Updates data list at LST-SYM in place using <a href="util-xdata.html#subst-key">subst-key</a></td>
</tr>
</tbody>
</table>
<section>
<h3 id="has-duplicate-keys-p" class="funcsig">(<span class="funcname">has-duplicate-keys-p</span> data)</h3>
<pre class="fulldoc">Returns T if any keys in data list DATA are repeated
VARS:
(DATA nil (DATA-LIST-P DATA))
TESTS:
(HAS-DUPLICATE-KEYS-P '(("a" 1) ("b" 2) ("a" 3)))
(NOT (HAS-DUPLICATE-KEYS-P '(("a" 1) ("b" 2))))</pre>
</section>
<section>
<h3 id="subst-key" class="funcsig">(<span class="funcname">subst-key</span> key new-val lst)</h3>
<pre class="fulldoc">Returns data list LST with value of KEY replaced with NEW-VAL
VARS:
(KEY STR)
(LST nil (DATA-LIST-P LST))
TESTS:
(EQUAL (SUBST-KEY 'A 2 '((A 1))) '((A 2)))
(EQUAL (SUBST-KEY 'A 2 '((B 1))) '((B 1)))</pre>
</section>
<section>
<h3 id="subst-key!" class="funcsig">(<span class="funcname">subst-key!</span> key new-val lst-sym)</h3>
<pre class="fulldoc">Updates data list at LST-SYM in place using <a href="util-xdata.html#subst-key">subst-key</a>
VARS:
(KEY STR)
(LST-SYM SYM (DATA-LIST-P (VL-SYMBOL-VALUE LST-SYM)))
TESTS:
(EQUAL ((LAMBDA (LST) (SUBST-KEY! 'A 2 'LST) LST) '((A 1))) '((A 2)))
(EQUAL ((LAMBDA (LST) (SUBST-KEY! 'A 2 'LST) LST) '((B 1))) '((B 1)))</pre>
</section>
</div>
</body>
</html>

@ -1,7 +1,7 @@
body {
box-sizing: border-box;
min-width: 200px;
max-width: 900px;
max-width: 80%;
margin-left: 10%;
padding: 45px;
-ms-text-size-adjust: 100%;
@ -46,6 +46,17 @@ pre {
font-size: 1em;
background-color: #ddd;
}
.fulldoc {
padding: 10px;
margin: 10px;
font-size: 1.1em;
}
.funcsig {
font-family: monospace;
}
.funcname {
color: #ff6969;
}
hr {
box-sizing: initial;
height: 0;
@ -128,7 +139,7 @@ h5,
h6 {
margin-top: 0;
margin-bottom: 0;
background-color: lightblue;
background-color: #9ef;
}
h1 {
font-size: 32px;
@ -393,3 +404,11 @@ pre code {
background-color: initial;
border: 0;
}
:target {
background-color: #7cd;
}
:target .funcname {
color: green;
}

@ -16,7 +16,7 @@
<style type="text/css">body {
box-sizing: border-box;
min-width: 200px;
max-width: 900px;
max-width: 80%;
margin-left: 10%;
padding: 45px;
-ms-text-size-adjust: 100%;
@ -61,6 +61,17 @@ font-family: monospace, monospace;
font-size: 1em;
background-color: #ddd;
}
.fulldoc {
padding: 10px;
margin: 10px;
font-size: 1.1em;
}
.funcsig {
font-family: monospace;
}
.funcname {
color: #ff6969;
}
hr {
box-sizing: initial;
height: 0;
@ -142,7 +153,7 @@ h5,
h6 {
margin-top: 0;
margin-bottom: 0;
background-color: lightblue;
background-color: #9ef;
}
h1 {
font-size: 32px;
@ -407,12 +418,18 @@ word-wrap: normal;
background-color: initial;
border: 0;
}
:target {
background-color: #7cd;
}
:target .funcname {
color: green;
}
</style>
</head>
<body>
<header id="title-block-header">
<h1 class="title">PSC AutoCAD Automation User Guide</h1>
<p class="date">06/14/23-07:33</p>
<p class="date">06/23/23-08:28</p>
</header>
<nav id="TOC" role="doc-toc">
<ul>
@ -479,7 +496,7 @@ border: 0;
<li><a href="#offset-and-close-a-polyline-in-both-directions">Offset and close a polyline in both directions</a></li>
<li><a href="#see-xdata-properties-of-a-special-block">See XDATA properties of a special block</a></li>
<li><a href="#double-click-action">Double-click action</a></li>
<li><a href="#debug-mode">Debug mode</a></li>
<li><a href="#development-mode">Development mode</a></li>
</ul></li>
</ul></li>
<li><a href="#list-of-commands">List of Commands</a>
@ -709,7 +726,7 @@ border: 0;
<li>Select stairs and/or landings</li>
<li>Press ENTER</li>
</ol>
<p>Note: if <a href="#debug-mode">debug mode</a> is enabled, fab drawings will be opened after creation. Use with caution when running big fab jobs.</p>
<p>Note: if <a href="#development-mode">development mode</a> is enabled, fab drawings will be opened after creation. Use with caution when running big fab jobs.</p>
<h3 id="insert-a-blank-fab-template">Insert a blank fab template</h3>
<p>The fab drawing templates are all available to be instantiated through the FABTEMPLATE command. This allows us to keep them with the code and update both in parallel. Dont copy them and use Ctrl+N to instantiate or you will miss out on updates and potentially break things in the future.</p>
<ol type="1">
@ -865,11 +882,11 @@ border: 0;
<li>In the right panel, edit the <strong>Macro</strong> field to say <code>editblock</code></li>
<li>Click OK</li>
</ol>
<h3 id="debug-mode">Debug mode</h3>
<h3 id="development-mode">Development mode</h3>
<ol type="1">
<li>Run DEBUG command to toggle mode on and off</li>
<li>Run DEVMODE command to toggle mode on and off</li>
</ol>
<p>When enabled, extra debugging statements will be printed to console, and fab drawings will be opened after creation.</p>
<p>When enabled, extra debugging statements will be printed to console, fab drawings will be opened after creation, and the code documentation building system will be turned on.</p>
<h1 id="list-of-commands">List of Commands</h1>
<h2 id="submittal-drawings">Submittal drawings</h2>
<table>
@ -960,6 +977,10 @@ border: 0;
</table>
<h2 id="fabrication-drawings">Fabrication drawings</h2>
<table>
<colgroup>
<col style="width: 17%"></col>
<col style="width: 82%"></col>
</colgroup>
<thead>
<tr class="header">
<th>Command</th>
@ -1015,6 +1036,10 @@ border: 0;
<td>UPDATE-CHK</td>
<td>Update fab drawings with checker initials</td>
</tr>
<tr class="odd">
<td>MARKPREFIX</td>
<td>Show the current material mark prefix and increment markprefix.txt</td>
</tr>
</tbody>
</table>
<h2 id="d-modeling-1">3D Modeling</h2>
@ -1074,8 +1099,8 @@ border: 0;
<td>File a feature request with the ticket system</td>
</tr>
<tr class="even">
<td>DEBUG</td>
<td>Enable or disable debug mode</td>
<td>DEVMODE</td>
<td>Enable or disable development mode</td>
</tr>
<tr class="odd">
<td>DRAWSHAPE</td>

@ -219,7 +219,7 @@ because it's used to fill in title blocks.
3. Select stairs and/or landings
4. Press ENTER
Note: if [debug mode](#debug-mode) is enabled, fab drawings will be opened after
Note: if [development mode](#development-mode) is enabled, fab drawings will be opened after
creation. Use with caution when running big fab jobs.
### Insert a blank fab template ###
@ -439,13 +439,12 @@ To edit stair and landing blocks by double-clicking:
4. Click OK
### Debug mode ###
### Development mode ###
1. Run DEBUG command to toggle mode on and off
When enabled, extra debugging statements will be printed to console, and fab drawings will
be opened after creation.
1. Run DEVMODE command to toggle mode on and off
When enabled, extra debugging statements will be printed to console, fab drawings will be
opened after creation, and the code documentation building system will be turned on.
# List of Commands #
## Submittal drawings
@ -506,7 +505,7 @@ be opened after creation.
| MANUAL | Open the User Guide in the browser |
| BUGREPORT | File a bug report with the ticket system |
| FEATUREREQ | File a feature request with the ticket system |
| DEBUG | Enable or disable debug mode |
| DEVMODE | Enable or disable development mode |
| DRAWSHAPE | Draw an AISC shape block |
| SHAPE-TEXT | Insert a text label for a DRAWSHAPE block |
| SHAPE-LINE | Insert a thickened line with text label |

@ -56,5 +56,5 @@
(vla-put-RegenerateTableSuppressed tbl :vlax-false)))
(vla-Regen acadDoc acAllViewports)
(vla-SaveAs acadDoc (strcat path "/" dwgno ".dwg"))
(if (not debug-flag) (vla-Close acadDoc))
(if (not *dev-mode*) (vla-Close acadDoc))
(reset-doc))

@ -158,7 +158,8 @@
(setq piece-mark (next-mark-prefix)
material-list (read-csv "fab/material.csv"))
(with-data data
'((if debug-flag
;; don't catch errors in dev mode
'((if *dev-mode*
(apply (read (strcat "make-" (to-string ent-type) "-fab")) nil)
(progn
(setq attempt (vl-catch-all-apply
@ -206,7 +207,7 @@
(vla-Save acadDoc)
(if error-p (setq path (vla-get-fullname acaddoc)))
(vla-Close acadDoc)
(if debug-flag
(if *dev-mode*
(vla-Open (vla-get-Documents acadObj) filename))
(setq acadDoc oldAcadDoc
modelSpace (vla-get-Modelspace acadDoc)))

@ -157,5 +157,5 @@
(vla-Regen acadDoc acAllViewports)
(vla-SaveAs acadDoc (strcat path "/" dwgno ".dwg"))
(if (not debug-flag) (vla-Close acadDoc))
(if (not *dev-mode*) (vla-Close acadDoc))
(reset-doc))

@ -128,5 +128,5 @@
(vla-Regen acadDoc acAllViewports)
(vla-SaveAs acadDoc (strcat path "/" dwgno ".dwg"))
(if (not debug-flag) (vla-Close acadDoc))
(if (not *dev-mode*) (vla-Close acadDoc))
(reset-doc))

@ -119,5 +119,5 @@
(vla-Regen acadDoc acAllViewports)
(vla-SaveAs acadDoc (strcat path "/" dwgno ".dwg"))
(if (not debug-flag) (vla-Close acadDoc))
(if (not *dev-mode*) (vla-Close acadDoc))
(reset-doc))

@ -90,5 +90,5 @@
(vla-Regen acadDoc acAllViewports)
(vla-SaveAs acadDoc (strcat path "/" dwgno ".dwg"))
(if (not debug-flag) (vla-Close acadDoc))
(if (not *dev-mode*) (vla-Close acadDoc))
(reset-doc))

@ -128,5 +128,5 @@
(vla-Regen acadDoc acAllViewports)
(vla-Save acadDoc)
(if (not debug-flag) (vla-Close acadDoc))
(if (not *dev-mode*) (vla-Close acadDoc))
(reset-doc))

@ -82,16 +82,16 @@
s1-line (tk '(line-offset (pt-slope (tan (dtr pitch)) 0 0) hg_offset))))
;; common proc to add a return based on centerline
(defun rail-return-add (cl bulge / frad tmp no-print-errors)
(defun rail-return-add (cl bulge / frad tmp *no-print-errors*)
;; attempt to fillet, but reduce radius if too big
(if (> (length cl) 2)
(progn
(setq frad 2.83
no-print-errors T)
*no-print-errors* T)
(while (vl-catch-all-error-p (setq tmp (vl-catch-all-apply 'fillet-all-pts (list cl frad))))
(setq frad (- frad 0.01)))
(setq cl tmp
no-print-errors nil)))
*no-print-errors* nil)))
(setq cl (add-polyline blockObj '(0 0 0) cl nil))
(if (tk '(= dir -1)) (vla-mirror-y cl))
(offset&close cl 0.83 T bulge)

@ -33,10 +33,8 @@
(offset&close
(add-polyline
add-to
(vl-sort
(list pt1 pt2)
'(lambda (pt1 pt2) (< (car pt1) (car pt2)))
) ;_ vl-sort
(car (vl-sort (list pt1 pt2)
(sort-by 'car '<)))
lst
nil
) ;_ add-polyline

@ -114,7 +114,7 @@
(setq ins
(line-int
(line-offset
(stair-slope data)
(stair-slope)
(if (= ascend "Left")
(+ (- l-string_dpt) l-nosing_mid -4)
(+ (- r-string_dpt) r-nosing_mid -4)

@ -1,36 +1,91 @@
(psc-include '("rail/common.lsp"))
(psc-include '("rail/common.lsp"
"shapes/line.lsp"
"stair/tread.lsp"))
;;; returns nosing line in slope intercept
;;; form calculated directly from stair XDATA
(defun stair-slope (data)
(psc-include '("stair/tread.lsp"))
(set-file-docstring "Utility functions for stairs.")
(defun-q
stair-dir (ascend z-position)
"Return the stair direction (either +1 or -1) based on ASCEND and Z-POSITION."
(declare (vars (ascend str
(member ascend '("Left" "Right")))
(z-position str
(member z-position '("Near" "Far"))))
(tests (= (stair-dir "Left" "Near") 1)
(= (stair-dir "Right" "Far") 1)
(= (stair-dir "Left" "Far") -1)
(= (stair-dir "Right" "Near") -1)))
(apply '* (mapcar '(lambda (x) (if (member x '("Near" "Left")) +1 -1))
(list ascend z-position))))
(defun-r 'stair-dir)
(defun-q
stair-slope ()
"Returns the nosing line in slope-intercept form."
(declare with-data)
(apply 'pt-slope
(cons
(tk '(/ rise_hgt (* dir tread_depth)))
(with-data data '((car (calc-nose-pts))))
) ;_ cons
) ;_ apply
) ;_ defun stair-slope
(cons (/ rise_hgt (* dir tread_depth))
(car (calc-nose-pts)))))
(defun-r 'stair-slope)
;; assembly name of HG return
(defun stair-hr-assembly ()
(expect-with-data "stair-hr-assembly")
(defun-q
stair-hr-assembly ()
"Returns the assembly name of the HG return for this stair."
(declare with-data
(vars (assembly str (wcmatch assembly "*S*"))))
(vl-string-subst "HR" "S" assembly))
(defun-r 'stair-hr-assembly)
;; assembly name of stair or wall rail
(defun stair-rail-assembly (side)
(expect-with-data "stair-sr-assembly")
(defun-q
stair-rail-assembly (side)
"Returns the assembly name of the stair rail on SIDE of this stair."
(declare with-data
(vars (side str (member side '("i" "o" "l" "r")))
(number str)
(level str)))
(strcat (if (wcmatch (sidekey-rail "type") "*Stair*") "S" "W")
"R" number "-" level (rail-sequence side)))
(defun-r 'stair-rail-assembly)
(defun-q
stair-guard-assembly (location)
"Returns the assembly name of the guard rail at LOCATION.
;; return the number of a guard rail based on its location and the
;; value of the stair's `guard_rails` key
;;
;; returns nil if no GR present at location
;; returns 0 if numbers are unneccessary (i.e. only one GR on stair)
;; otherwise, returns the index number of the GR at loc
(defun stair-guard-num (loc / hamming setp codex)
(expect-with-data "stair-guard-num")
LOCATION should be one of the following strings:
- \"ibot\" (inside bottom)
- \"obot\" (outside bottom)
- \"itop\" (inside top)
- \"otop\" (outside top)
- \"btwn\" (between stairs)"
(declare with-data
(vars (location str (member location '("ibot" "obot" "itop" "otop" "btwn")))
(number str)
(level str)
(sequence str)))
(strcat "GR"
number
"-"
level
sequence
((lambda(/ num)
(if (and (setq num (stair-guard--num location))
(> num 0))
(strcat "-" (to-string num))
"")))))
(defun-r 'stair-guard-assembly)
(defun-q
stair-guard--num (loc / hamming setp codex)
"Returns the number of a guard rail
This function calculates the -N at the end of a stair guard rail's sequence number based
on location LOC. See %stair-guard-assembly for format of LOC.
If there is no guard rail at LOC, returns nil. If there is only one guard rail on the
flight, returns 0, indicating numbers are unnecessary."
(declare with-data
(vars (loc str (member location '("ibot" "obot" "itop" "otop" "btwn")))))
;; hamming weight of a bit string
(defun hamming (n / ret)
(setq ret 0)
@ -51,17 +106,18 @@
(if (setp loc)
(1+ (hamming (logand guard_rails (1- (cdr (assoc loc codex))))))))
(if (setp loc) 0)))
(defun-r 'stair-guard--num)
;; return a GR's full assembly string, including an index number, if necessary
(defun stair-guard-assembly (location)
(expect-with-data "stair-guard-assembly")
(strcat "GR"
number
"-"
level
sequence
(if (and (setq num (stair-guard-num location))
(> num 0))
(strcat "-" (to-string num))
"")))
;;;;;;;;;;;
;; tests ;;
;;;;;;;;;;;
(if *test-build-p*
(progn
(defun-q
test-stair-hr-assembly (/ from-with-data assembly)
"stair-hr-assembly"
(setq from-with-data t
assembly "S1-1A")
(assert-all '((= (stair-hr-assembly) "HR1-1A"))))
(defun-t 'test-stair-hr-assembly)))

@ -0,0 +1,244 @@
(psc-include (list "util/arithmetic.lsp"))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (SQUARE 2) 4) (= (SQUARE -3) 9))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-square: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((NOT (IS-EVEN 1)) (IS-EVEN 2))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-is-even: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (TAN 0) 0.0))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-tan: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (CEIL 0.1) 1) (= (CEIL 1) 1) (= (CEIL -0.9) 0))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-ceil: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (RND-DN 1.3 0.25) 1.25) (= (RND-DN 1.9 0.5) 1.5))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-rnd-dn: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (RND-UP 1.1 0.25) 1.25) (= (RND-UP 1.1 0.5) 1.5))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-rnd-up: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
(psc-include (list "util/comparison.lsp"))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (IDENTITY (QUOTE X)) (QUOTE X)))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-identity: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (SORT-BY (QUOTE CAR) (QUOTE <)) (QUOTE (LAMBDA (A B) (< (CAR A) (CAR B))))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-sort-by: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (OR* (QUOTE (nil 1 2))) 1) (= (OR* (QUOTE (nil nil))) nil))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-or*: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
(psc-include (list "util/function.lsp"))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((FUNCTION-P +) (NOT (FUNCTION-P (QUOTE (LAMBDA nil 1)))) (NOT (FUNCTION-P 123)))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-function-p: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((FUNCTION-OR-LAMBDA-P +) (FUNCTION-OR-LAMBDA-P (QUOTE (LAMBDA nil 1))) (NOT (FUNCTION-OR-LAMBDA-P 123)))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-function-or-lambda-p: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (APPLY-BY-TWOS (QUOTE +) (QUOTE (1 2 3))) (QUOTE (3 5))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-apply-by-twos: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (MAP-APPLY (QUOTE +) (QUOTE ((1 2) (3 4)))) (QUOTE (3 7))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-map-apply: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
(psc-include (list "util/geometry.lsp"))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((WITHIN-BOX-P (QUOTE (0 0)) (QUOTE (-1 -1)) (QUOTE (1 1))) (NOT (WITHIN-BOX-P (QUOTE (10 10)) (QUOTE (0 0)) (QUOTE (1 1)))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-within-box-p: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (RECT-PTS 1 1 (QUOTE (0 0))) (QUOTE ((0 0) (1 0) (1 1) (0 1)))) (EQUAL (RECT-PTS 1 2 (QUOTE (3 4))) (QUOTE ((3 4) (4 4) (4 6) (3 6)))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-rect-pts: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (MIRROR-PTS-V (QUOTE ((0 0) (1 1))) 0) (QUOTE ((0 0) (-1 1)))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-mirror-pts-v: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (MIRROR-PTS-H (QUOTE ((0 0) (1 1))) 0) (QUOTE ((0 0) (1 -1)))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-mirror-pts-h: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (REZERO-PTS (QUOTE ((0 0) (1 1))) 1) (QUOTE ((0 0 0) (-1 -1 0)))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-rezero-pts: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (DISPLACE-PT (QUOTE (0 0)) (QUOTE (2 2))) (QUOTE (2 2))) (EQUAL (DISPLACE-PT (QUOTE (1 2)) (QUOTE (3 4))) (QUOTE (4 6))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-displace-pt: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (DISPLACE-PTS (QUOTE ((0 0) (1 1))) (QUOTE (2 2))) (QUOTE ((2 2) (3 3)))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-displace-pts: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (JOIN-VERTICES (QUOTE (((0 0) (1 0)) ((1 0) (1 1)) ((1 2) (0 2))))) (QUOTE (((0 0) (1 0) (1 1)) ((1 2) (0 2))))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-join-vertices: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
(psc-include (list "util/list.lsp"))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL ((LAMBDA (LST) (POP! (QUOTE LST))) nil) nil) (EQUAL ((LAMBDA (LST) (POP! (QUOTE LST))) (QUOTE (1))) 1) (EQUAL ((LAMBDA (LST) (POP! (QUOTE LST)) LST) (QUOTE (1 2))) (QUOTE (2))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-pop!: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (RCONS (QUOTE (1)) 2) (QUOTE (1 2))) (EQUAL (RCONS nil 1) (QUOTE (1))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-rcons: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (TAKE 1 (QUOTE (1 2))) (QUOTE (1))) (EQUAL (TAKE 2 (QUOTE (1))) (QUOTE (1))) (= (TAKE 1 nil) nil))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-take: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (TAKE-WHILE (QUOTE NUMBERP) (QUOTE (1 2 nil 3))) (QUOTE (1 2))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-take-while: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL ((LAMBDA (LST) (TAKE! 1 (QUOTE LST))) (QUOTE (1 2))) (QUOTE (1))) (EQUAL ((LAMBDA (LST) (TAKE! 1 (QUOTE LST)) LST) (QUOTE (1 2))) (QUOTE (2))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-take!: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (ZIP (QUOTE (1 2)) (QUOTE (A B))) (QUOTE (1 A 2 B))) (EQUAL (ZIP (QUOTE (1 2 3)) (QUOTE (A))) (QUOTE (1 A 2 3))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-zip: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL ((LAMBDA (/ LST) (ADD-TO-ALIST (QUOTE LST) (QUOTE A) 1 nil))) (QUOTE ((A . 1)))) (EQUAL ((LAMBDA (LST) (ADD-TO-ALIST (QUOTE LST) (QUOTE A) 1 nil)) (QUOTE ((A . 2)))) (QUOTE ((A . 1)))) (EQUAL ((LAMBDA (LST) (ADD-TO-ALIST (QUOTE LST) (QUOTE A) ", world" T)) (QUOTE ((A . "hello")))) (QUOTE ((A . "hello, world")))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-add-to-alist: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL ((LAMBDA (/ LST) (ADD-TO-LIST (QUOTE LST) 1))) (QUOTE (1))) (EQUAL ((LAMBDA (LST) (ADD-TO-LIST (QUOTE LST) 2)) (QUOTE (1))) (QUOTE (1 2))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-add-to-list: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL ((LAMBDA (LST) (ADD-TO-LIST-AT-POS (QUOTE LST) nil 1)) (QUOTE (1 2))) (QUOTE (1 nil 2))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-add-to-list-at-pos: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (REMOVE-NTH 1 (QUOTE (1 2 3))) (QUOTE (1 3))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-remove-nth: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL ((LAMBDA (LST) (SUBST! (QUOTE A) 1 (QUOTE LST)) LST) (QUOTE (1))) (QUOTE (A))) (EQUAL ((LAMBDA (LST) (SUBST! (QUOTE A) 1 (QUOTE LST)) LST) (QUOTE (2))) (QUOTE (2))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-subst!: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (REMOVE (QUOTE (1 2 3)) (QUOTE (> X 1))) (QUOTE (1))) (EQUAL (REMOVE (QUOTE (1 2 3)) (QUOTE (< X 1))) (QUOTE (1 2 3))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-remove: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL ((LAMBDA (LST) (REMOVE! (QUOTE LST) (QUOTE (> X 1))) LST) (QUOTE (1 2 3))) (QUOTE (1))) (EQUAL ((LAMBDA (LST) (REMOVE! (QUOTE LST) (QUOTE (< X 1))) LST) (QUOTE (1 2 3))) (QUOTE (1 2 3))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-remove!: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (FILTER (QUOTE (1 2 3)) (QUOTE (< X 2))) (QUOTE (1))) (EQUAL (FILTER (QUOTE (1 2 3)) (QUOTE (> X 0))) (QUOTE (1 2 3))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-filter: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL ((LAMBDA (LST) (SORT! (QUOTE LST) (QUOTE <)) LST) (QUOTE (2 1 3))) (QUOTE (1 2 3))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-sort!: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (SAFE-NTH 1 (QUOTE (1 2))) 2) (= (SAFE-NTH 1 nil) nil))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-safe-nth: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((QUOTE (EQUAL (UNIQUIFY (QUOTE (1 2 3 1 2))) (QUOTE (1 2 3)))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-uniquify: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL ((LAMBDA (LST) (MAPCAR! (QUOTE 1+) (QUOTE LST)) LST) (QUOTE (1 2 3))) (QUOTE (2 3 4))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-mapcar!: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (MAP-APPEND (QUOTE REVERSE) (QUOTE ((1 2) (3 4)))) (QUOTE (2 1 4 3))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-map-append: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((TEST-FOR (QUOTE INT) (QUOTE (1 2 3))) (TEST-FOR (QUOTE SYM) (QUOTE (A B C))) (NOT (TEST-FOR (QUOTE INT) (QUOTE (1 2 3.0)))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-test-for: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL ((LAMBDA (LST) (APPEND! (QUOTE LST) (QUOTE (3))) LST) (QUOTE (1 2))) (QUOTE (1 2 3))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-append!: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (RPT-LST 3 (QUOTE Q)) (QUOTE (Q Q Q))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-rpt-lst: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (LST* (QUOTE Q) 3) (QUOTE (Q Q Q))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-lst*: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (REMOVE-LAST (QUOTE (1 2 3))) (QUOTE (1 2))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-remove-last: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (CIRCULAR-NTH 1 (QUOTE (1 2 3))) 2) (= (CIRCULAR-NTH -1 (QUOTE (1 2 3))) 3) (= (CIRCULAR-NTH 3 (QUOTE (1 2 3))) 1) (= (CIRCULAR-NTH 6 (QUOTE (1 2 3))) 1))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-circular-nth: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (CIRCULAR-SHIFT (QUOTE (1 2 3)) 1) (QUOTE (2 3 1))) (EQUAL (CIRCULAR-SHIFT (QUOTE (1 2 3)) 2) (QUOTE (3 1 2))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-circular-shift: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (FIND-FIRST (QUOTE (LAMBDA (A) (> A 1))) (QUOTE (1 2 3))) 2))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-find-first: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (COUNT-ITEMS (QUOTE (1))) (QUOTE ((1 1)))) (EQUAL (COUNT-ITEMS (QUOTE (1 1 2 2 2))) (QUOTE ((1 2) (2 3)))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-count-items: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (RANGE 1 4 1) (QUOTE (1 2 3))) (EQUAL (RANGE 0.75 1.25 0.125) (QUOTE (0.75 0.875 1.0 1.125))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-range: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (CONS-IND-LST 3 nil) (QUOTE (0 1 2))) (EQUAL (CONS-IND-LST 3 5) (QUOTE (5 6 7))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-cons-ind-lst: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (2-ITEM-LIST (QUOTE (1 2 A B))) (QUOTE ((1 2) (A B)))) (EQUAL (2-ITEM-LIST (QUOTE (1 2 A))) (QUOTE ((1 2) (A nil)))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-2-item-list: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (3-ITEM-LIST (QUOTE (1 2 3 A B C))) (QUOTE ((1 2 3) (A B C)))) (EQUAL (3-ITEM-LIST (QUOTE (1 2 3 A B))) (QUOTE ((1 2 3) (A B nil)))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-3-item-list: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (FIRST-TWO (QUOTE (1 2 3))) (QUOTE (1 2))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-first-two: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (STRIP-NIL (QUOTE (1 nil 2))) (QUOTE (1 2))) (EQUAL (STRIP-NIL (QUOTE (1 2 3))) (QUOTE (1 2 3))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-strip-nil: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (SLICE (QUOTE (1 2 3 4)) 0 4) (QUOTE (1 2 3 4))) (EQUAL (SLICE (QUOTE (1 2 3 4)) 1 2) (QUOTE (2 3))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-slice: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((MEMBER (QUOTE A) (QUOTE (A B))) (NOT (MEMBER (QUOTE C) (QUOTE (A B)))) (MEMBER* (QUOTE (A 1)) (QUOTE (A B))) (MEMBER* (QUOTE (A 1)) (QUOTE (1 2))) (NOT (MEMBER* (QUOTE (A B)) (QUOTE (1 2)))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-member*: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
(psc-include (list "util/selection.lsp"))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (DXF-FILTER (QUOTE ((APPID "Land")))) (QUOTE ((-3 ("Land"))))) (EQUAL (DXF-FILTER (QUOTE ((AND (TYPE "CIRCLE") (LAYER "DIM"))))) (QUOTE ((-4 . "<AND") (0 . "CIRCLE") (8 . "DIM") (-4 . "AND>")))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-dxf-filter: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
(psc-include (list "util/string.lsp"))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((STRINGP "hello") (NOT (STRINGP 1)))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-stringp: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (STRJOIN (QUOTE ("key" "val")) "=") "key=val") (= (STRJOIN (QUOTE ("hello" "world")) ", ") "hello, world"))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-strjoin: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (STRSPLIT "key=val" "=") (QUOTE ("key" "val"))) (EQUAL (STRSPLIT "hello, world" ", ") (QUOTE ("hello" "world"))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-strsplit: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (STRBREAK "hi") (QUOTE ("h" "i"))) (= (STRBREAK "") nil))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-strbreak: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (WCMATCH-ESCAPE "what # is 1*1?") "what `# is 1`*1`?") (= (WCMATCH-ESCAPE "backtick[`]@my-mail.com~") "backtick`[```]`@my`-mail`.com`~"))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-wcmatch-escape: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((BEGINS-WITH "hello" "h") (BEGINS-WITH "hello" "") (NOT (BEGINS-WITH "hello" "a")))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-begins-with: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((ENDS-WITH "hello" "o") (ENDS-WITH "hello" "") (NOT (ENDS-WITH "hello" "a")))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-ends-with: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (LOWERCASE "hi") "hi") (= (LOWERCASE "HI") "hi"))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-lowercase: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (UPPERCASE "HI") "HI") (= (UPPERCASE "hi") "HI"))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-uppercase: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (STRING-SUBST-ALL "hello" "hi" "hi hi") "hello hello"))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-string-subst-all: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (FIRST-LINE "hi") "hi") (= (FIRST-LINE "item1\nitem2") "item1"))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-first-line: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (ESCAPE-NEWLINES "hi") "hi") (= (ESCAPE-NEWLINES "item1\nitem2") "item1\\nitem2"))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-escape-newlines: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
(psc-include (list "util/xdata.lsp"))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL (SUBST-KEY (QUOTE A) 2 (QUOTE ((A 1)))) (QUOTE ((A 2)))) (EQUAL (SUBST-KEY (QUOTE A) 2 (QUOTE ((B 1)))) (QUOTE ((B 1)))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-subst-key: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((EQUAL ((LAMBDA (LST) (SUBST-KEY! (QUOTE A) 2 (QUOTE LST)) LST) (QUOTE ((A 1)))) (QUOTE ((A 2)))) (EQUAL ((LAMBDA (LST) (SUBST-KEY! (QUOTE A) 2 (QUOTE LST)) LST) (QUOTE ((B 1)))) (QUOTE ((B 1)))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-subst-key!: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((HAS-DUPLICATE-KEYS-P (QUOTE (("a" 1) ("b" 2) ("a" 3)))) (NOT (HAS-DUPLICATE-KEYS-P (QUOTE (("a" 1) ("b" 2))))))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-has-duplicate-keys-p: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
(psc-include (list "stair/util.lsp"))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (ASSERT-ALL (QUOTE ((= (STAIR-DIR "Left" "Near") 1) (= (STAIR-DIR "Right" "Far") 1) (= (STAIR-DIR "Left" "Far") -1) (= (STAIR-DIR "Right" "Near") -1))))))) (IF ERRORS (PROGN (PRINC "\nTEST autotest-stair-dir: declare autotest\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
;;
((LAMBDA (/ ERRORS) (SETQ ERRORS ((LAMBDA (/ FROM-WITH-DATA ASSEMBLY *ASSERT-RETURN-ERROR*) (SETQ *ASSERT-RETURN-ERROR* T) (SETQ FROM-WITH-DATA T ASSEMBLY "S1-1A") (ASSERT-ALL (QUOTE ((= (STAIR-HR-ASSEMBLY) "HR1-1A"))))))) (IF ERRORS (PROGN (PRINC "\nTEST test-stair-hr-assembly: stair-hr-assembly\n") (PRINC (STRJOIN ERRORS "\n")) (PRINC "\n") (SETQ N-ERRORS (+ N-ERRORS (LENGTH ERRORS)))))))
(princ)

@ -2,7 +2,7 @@
;; this file is automatically loaded into every new document namespace
(defun util-load (name)
(load (strcat "util/" name ".lsp")))
(psc-load (strcat "util/" name ".lsp")))
(mapcar 'util-load '(
"alias" ; convenience renamings and wrappers
@ -15,6 +15,7 @@
"function" ; function-handling functions
"geometry" ; points, angles, planes, pipe bending
"job" ; related to job folder
"json" ; Javascript Object Notation generation
"list" ; list-handling functions
"object" ; entity-handling functions index
"selection" ; selection sets
@ -22,4 +23,6 @@
"symbol" ; symbol manipulation
"time" ; time and date
"xdata" ; xdata and data list handling
"documentation" ; functions pertaining to documenting the code
"test" ; functions pertaining to testing the code
))

@ -1,17 +1,88 @@
(set-file-docstring
"Aliases and wrappers")
(setq ename>vlobj vlax-ename->vla-object
vlobj>ename vlax-vla-object->ename
3dpt vlax-3d-point)
;; these functions appended with "-r" duplicate the base vla-XXX
;; functions but return the object name the function was applied
;; to which allows for easier nesting of vla-XXX methods
(defun vlax-put-property-r (obj pro val) (vlax-put-property obj pro val) obj)
(defun vla-put-layer-r (obj lay) (vla-put-Layer obj lay) obj)
(defun vla-put-color-r (obj col) (vla-put-color obj col) obj)
(defun vla-rotate-r (obj ins rot) (vla-rotate obj ins rot) obj)
(defun vla-rotate3d-r (obj ax1 ax2 rot) (vla-rotate3d obj ax1 ax2 rot) obj)
(defun vla-transformby-r (obj mat) (vla-transformby obj mat) obj)
(defun vla-boolean-r (ob1 bop ob2) (vla-boolean ob1 bop ob2) ob1)
(defun vla-put-normal-r (obj nor) (vla-put-normal obj nor) obj)
(defun vla-logic (e) (if e :vlax-true :vlax-false))
(defun-q
vlax-put-property-r (obj pro val)
"Wrapper for vlax-put-property that returns OBJ"
(declare (vars (obj vla-object)
(pro (str sym))))
(vlax-put-property obj pro val)
obj)
(defun-r 'vlax-put-property-r)
(defun-q
vla-put-layer-r (obj lay)
"Wrapper for vla-put-layer that returns OBJ"
(declare (vars (obj vla-object)
(lay str)))
(vla-put-Layer obj lay)
obj)
(defun-r 'vla-put-layer-r)
(defun-q
vla-put-color-r (obj col)
"Wrapper for vla-put-color that returns OBJ"
(declare (vars (obj vla-object)
(col int (>= col 0) (<= col 256))))
(vla-put-color obj col)
obj)
(defun-r 'vla-put-color-r)
(defun-q
vla-rotate-r (obj ins rot)
"Wrapper for vla-rotate that returns OBJ"
(declare (vars (obj vla-object)
(ins variant)
(rot nil (numberp rot))))
(vla-rotate obj ins rot)
obj)
(defun-r 'vla-rotate-r)
(defun-q
vla-rotate3d-r (obj ax1 ax2 rot)
"Wrapper for vla-rotate3d that returns OBJ"
(declare (vars (obj vla-object)
(ax1 variant)
(ax2 variant)
(rot nil (numberp rot))))
(vla-rotate3d obj ax1 ax2 rot)
obj)
(defun-r 'vla-rotate3d-r)
(defun-q
vla-transformby-r (obj mat)
"Wrapper for vla-transformby that returns OBJ"
(declare (vars (obj vla-object)
(mat variant)))
(vla-transformby obj mat)
obj)
(defun-r 'vla-transformby-r)
(defun-q
vla-boolean-r (ob1 bop ob2)
"Wrapper for vla-boolean that returns OB1"
(declare (vars (ob1 vla-object)
(bop nil (member bop (list acUnion acIntersection acSubtraction)))
(ob2 vla-object)))
(vla-boolean ob1 bop ob2)
ob1)
(defun-r 'vla-boolean-r)
(defun-q
vla-put-normal-r (obj nor)
"Wrapper for vla-put-normal that returns OBJ"
(declare (vars (obj vla-object)
(nor variant)))
(vla-put-normal obj nor)
obj)
(defun-r 'vla-put-normal-r)
(defun-q
vla-logic (e)
"Returns :vlax-true or :vlax-false based on truthiness of E"
(if e :vlax-true :vlax-false))
(defun-r 'vla-logic)

@ -1,51 +1,107 @@
(defun square (n) (* n n))
(set-file-docstring
"Functions related to basic math")
;; increment int-sym
(defun inc! (int-sym)
(chkargs "inc!" '((int-sym sym ((numberp (vl-symbol-value int-sym))))))
(defun-q
square (n)
"Returns the square of N"
(declare (vars (n nil (numberp n)))
(tests (= (square 2) 4)
(= (square -3) 9)))
(* n n))
(defun-r 'square)
(defun-q
is-even (n)
"Returns nil if N is not divisible by 2"
(declare (vars (n int))
(tests (not (is-even 1))
(is-even 2)))
(zerop (rem n 2.0)))
(defun-r 'is-even)
;;;;;;;;;;;;;;;;;;;;;;;;;
;; increment/decrement ;;
;;;;;;;;;;;;;;;;;;;;;;;;;
(defun-q
inc! (int-sym)
"Increments INT-SYM destructively"
(set int-sym (1+ (vl-symbol-value int-sym))))
(declare-late 'inc!
'((vars (int-sym sym (numberp (vl-symbol-value int-sym))))))
(defun-r 'inc!)
;; return value of int-sym and then increment it
(defun post-inc! (int-sym / val)
(defun-q
post-inc! (int-sym / val)
"Returns the value of INT-SYM then increment it destructively"
(declare (vars (int-sym sym (numberp (vl-symbol-value int-sym)))))
(1- (inc! int-sym)))
(defun-r 'post-inc!)
;; decrement int-sym
(defun dec! (int-sym)
(chkargs "dec!" '((int-sym sym ((numberp (vl-symbol-value int-sym))))))
(defun-q
dec! (int-sym)
"Decrements INT-SYM destructively"
(declare (vars (int-sym sym (numberp (vl-symbol-value int-sym)))))
(set int-sym (1- (vl-symbol-value int-sym))))
(defun-r 'dec!)
;; return value of int-sym and then decrement it
(defun post-inc! (int-sym / val)
(defun-q
post-dec! (int-sym / val)
"Returns the value of INT-SYM then decrement it destructively"
(declare (vars (int-sym sym (numberp (vl-symbol-value int-sym)))))
(1- (inc! int-sym)))
(defun-r 'post-dec!)
;; tangent function (a in radians)
(defun tan (a)
(/ (sin a) (cos a)))
;;;;;;;;;;;;;;;;;;;;
;; trig functions ;;
;;;;;;;;;;;;;;;;;;;;
;; ceiling
(defun ceil (n / fn)
(setq fn (fix n))
(if (> n fn) (1+ fn) fn))
(defun-q
tan (ang)
"Returns the tangent of ANG (radians)"
(declare (vars (ang nil (numberp ang)))
(tests (= (tan 0) 0.0)))
(/ (sin ang) (cos ang)))
(defun-r 'tan)
;; round a number up or down to the next multiple of precision
(defun rnd-dn (num prc) (- num (rem num (float prc))))
(defun rnd-up (num prc) (- (+ num prc) (rem num prc)))
(defun-q
acos (ang)
"Returns the arccosine of ANG (radians)"
(declare (vars (ang nil (numberp ang))))
(atan (/ (sqrt (- 1 (* ang ang))) ang)))
(defun-r 'acos)
;; return t if i is even
(defun is-even (i) (chkargs "is-even" '((i int))) (zerop (rem i 2.0)))
;;;;;;;;;;;;;;
;; rounding ;;
;;;;;;;;;;;;;;
(defun stair-dir (l/r n/f)
(chkargs "stair-dir" '((l/r str ((member (uppercase l/r) '("LEFT" "RIGHT"))))
(n/f str ((member (uppercase n/f) '("NEAR" "FAR"))))))
(apply '*
(mapcar
'(lambda (x) (if (member x '("NEAR" "LEFT")) +1 -1))
(mapcar 'strcase (list l/r n/f))
) ;_ mapcar
) ;_ apply
) ;_ defun
(defun-q
ceil (n / fn)
"Returns the smallest integer greater than or equal to N"
(declare (vars (n nil (numberp n)))
(tests (= (ceil 0.1) 1)
(= (ceil 1) 1)
(= (ceil -0.9) 0)))
(setq fn (fix n))
(if (> n fn) (1+ fn) fn))
(defun-r 'ceil)
;; arccosine
(defun acos (x)
(atan (/ (sqrt (- 1 (* x x))) x)))
(defun-q
rnd-dn (num prc)
"Rounds NUM down to the next multiple of precision PRC"
(declare (vars (num nil (numberp num))
(prc nil (numberp prc)))
(tests (= (rnd-dn 1.3 0.25) 1.25)
(= (rnd-dn 1.9 0.5) 1.5)))
(- num (rem num (float prc))))
(defun-r 'rnd-dn)
(defun-q
rnd-up (num prc)
"Rounds NUM up to the next multiple of precision PRC"
(declare (vars (num nil (numberp num))
(prc nil (numberp prc)))
(tests (= (rnd-up 1.1 0.25) 1.25)
(= (rnd-up 1.1 0.5) 1.5)))
(- (+ num prc) (rem num prc)))
(defun-r 'rnd-up)

@ -1,53 +1,103 @@
;; return x
(defun identity(x) x)
(set-file-docstring
"Functions related to comparisons")
;; return a lambda sufficient for passing to vl-sort
(defun sort-by (func cmp)
(defun-q
identity (x)
"Returns X."
(declare (tests (= (identity 'x) 'x)))
x)
(defun-r 'identity)
(defun-q
sort-by (func cmp)
"Returns a lambda for processed sorting
The result of this function is a lambda suitable for passing to vl-sort. CMP should be a
comparison function of 2 arguments that would normally be passed to vl-sort. FUNC should
be a function of 1 argument that will be used to process both arguments to CMP.
Returns the equivalent of '(lambda (a b) (CMP (FUNC a) (FUNC b)))."
(declare (vars (func (sym list subr usubr))
(cmp (sym list subr usubr)))
(tests (equal (sort-by 'car '<)
'(lambda (a b) (< (car a) (car b))))))
(append '(lambda (a b))
(#! '((#cmp (#func a) (#func b))))))
(defun-r 'sort-by)
;; return a lambda sufficient for passing to vl-sort that sorts a data
;; list on a given key
(defun sort-by-key (key cmp)
(defun-q
sort-by-key (key cmp)
"Returns a lambda for sorting a data list by the value of KEY
The result of this function is a lambda suitable for passing to vl-sort. CMP should be a
comparison function of 2 arguments that would normally be passed to vl-sort. KEY should be
a key from the data list as a string."
(declare (vars (key str)
(cmp (sym list subr usubr))))
(append '(lambda (a b))
(#! '((#cmp (value #key a) (value #key b))))))
(defun-r 'sort-by-key)
(defun-q
sort-strings-asc (a b)
"A comparison function that accounts for length when sorting strings
;; a comparison function suitable for vl-sort that sorts strings
;; accounting for length as well as simple lexicography provided by <
(defun sort-strings-asc (a b)
Pass as comparison argument to vl-sort."
(if (= (strlen a) (strlen b))
(< a b)
(< (strlen a) (strlen b))))
(defun-r 'sort-strings-asc)
;; for use with vl-sort
(setq sort-data-by-key '(lambda(a b) (< (car a) (car b))))
;; float comparison operators with fuzz
(defun >f (a b fuzz) (> (- a b) fuzz))
(defun <f (a b fuzz) (> (- b a) fuzz))
(defun =f (a b fuzz) (< (abs (- a b)) fuzz))
;; sort pair of stair/landing enames in ascending order
(defun sort-stairland (a b / ay az by bz)
(setq
ay (caddr (assoc 10 (entget a)))
az (cadddr (assoc 10 (entget a)))
by (caddr (assoc 10 (entget b)))
bz (cadddr (assoc 10 (entget b)))
) ;_ setq
(defun-q
>f (a b fuzz)
"Returns nil unless A is greater than B by FUZZ"
(> (- a b) fuzz))
(defun-r '>f)
(defun-q
<f (a b fuzz)
"Returns nil unless A is less than B by FUZZ"
(> (- b a) fuzz))
(defun-r '<f)
(defun-q
=f (a b fuzz)
"Return nil unless A and B are equal within FUZZ"
(< (abs (- a b)) fuzz))
(defun-r '=f)
(defun-q
sort-stairland (a b / ay az by bz)
"Sorts a part of stair/landing enames in ascending order.
This function is designed to be passed to vl-sort. The resulting sort order will sort by
elevation and put landings in front of stairs at the same level. Multiple landings at the
same level are not considered."
(setq ay (caddr (assoc 10 (entget a)))
az (cadddr (assoc 10 (entget a)))
by (caddr (assoc 10 (entget b)))
bz (cadddr (assoc 10 (entget b))))
(if (wcmatch (caadr (assoc -3 (entget a '("*")))) "*3D*")
(if (< (abs (- az bz)) 0.001)
(= (caadr (assoc -3 (entget b '("*")))) "3D_Stair")
(< az bz)
) ;_ if
(if (< (abs (- ay by)) 0.001)
(= (caadr (assoc -3 (entget b '("*")))) "Stair")
(< ay by)
) ;_ if
) ;_ if
) ;_ defun sort-stairland
;; variant of or that returns the first true value
(defun or* (branches)
(if (< (abs (- az bz)) 0.001)
(= (caadr (assoc -3 (entget b '("*")))) "3D_Stair")
(< az bz))
(if (< (abs (- ay by)) 0.001)
(= (caadr (assoc -3 (entget b '("*")))) "Stair")
(< ay by))))
(defun-r 'sort-stairland)
(defun-q
or* (branches)
"Returns the first non-nil element of BRANCHES.
or* doesn't eval the branches, so doesn't do short-circuiting."
(declare (vars (branches nil (listp branches)))
(tests (= (or* '(nil 1 2)) 1)
(= (or* '(nil nil)) nil)))
(while (and branches (not (car branches))) (pop! 'branches))
(car branches))
(defun-r 'or*)

@ -1,63 +1,126 @@
;; like (vl-princ-to-string x) but returns nil for nil, rather than "nil"
(defun to-string (x)
(if (defined x)
(if (= (type x) 'STR)
(set-file-docstring
"Functions related to conversions")
(defun-q
to-string (x)
"Returns X as a string
Essentially a wrapper for vl-prin1-to-string that returns the empty string for nil rather
than \"nil\"."
(if x (if (stringp x)
x
(vl-prin1-to-string x))
""))
""))
(defun-r 'to-string)
(defun-q
safe-atoi (str / )
"Wrapper for atoi that returns nil if there was no number
;; normal atoi returns 0 if the string doesn't start with a number
;; this version returns nil in that case
(defun safe-atoi (str / )
(atoi \"\") => 0
(safe-atoi \"\") => nil"
(if (wcmatch str "#*")
(atoi str)
nil))
nil))
(defun-r 'safe-atoi)
;; degrees to radians
(defun dtr (a)
(* pi (/ a 180.0)))
(defun-q
dtr (rad)
"Returns RAD in radians"
(declare (vars (rad (int real))))
(* pi (/ rad 180.0)))
(defun-r 'dtr)
;; radians to degrees
(defun rtd (a)
(/ (* a 180.0) pi))
(defun-q
rtd (deg)
"Return DEG in degrees"
(/ (* deg 180.0) pi))
(defun-r 'rtd)
;; convert a collection to a list of names
;; the items in the collection must support a name property
(defun vlax-collection->lst (collection / ret)
(defun-q
vlax-collection->lst (collection / ret)
"Convert COLLECTION to a list of names
COLLECTION should be a vlax collection of objects that support a Name property."
(declare (vars (collection vla-object)))
(vlax-for x collection (add-to-list 'ret (vla-get-Name x))))
(defun-r 'vlax-collection->lst)
;; convenience function to convert to fractional string
(defun frac (n)
(chkargs "frac" '((n nil (numberp))))
(defun-q
frac (n)
"Alias for (rtos n 5 2)"
;; (declare (vars (n (int real)))
;; (tests (= (frac 0.25) "1/4")))
(rtos n 5 4))
(defun-r 'frac)
(defun-q
? (x)
"Alias for (if X X)"
(if x x))
(defun-r '?)
;; syntactic sugar
(defun ? (x) (if x x))
(defun ?? (x default) (if x x default))
(defun ?n (n) (if n n 0))
(defun ?f (n) (if n n 0.0))
(defun ?s (s) (if s s ""))
(defun-q
?? (x default)
"Alias for (if X X DEFAULT)"
(if x x default))
(defun-r '??)
;; return 1 if positive, -1 if negative, 0 if 0
(defun unit-dir (n)
(defun-q
?n (n)
"Alias for (if N N 0)"
(if n n 0))
(defun-r '?n)
(defun-q
?f (n)
"Alias for (if N N 0.0)"
(if n n 0.0))
(defun-r '?f)
(defun-q
?s (s)
"Alias for (if S S \"\")"
(if s s ""))
(defun-r '?s)
(defun-q
unit-dir (n)
"Returns 0 if N is 0, 1 if N is positive, or -1 if N is negative
Uses a precision fuzz of 0.00001 for comparison. See %>f."
;; (declare (vars (n nil (numberp n)))
;; (tests (= (unit-dir 0) 0)
;; (= (unit-dir 10) 1)
;; (= (unit-dir -10) -1)))
(cond ((>f n 0 0.00001) 1)
((<f n 0 0.00001) -1)
(T 0)))
(defun-r 'unit-dir)
(defun-q
num-val (x)
"Alias for (if (numberp X) X 0)"
(if (numberp x) x 0))
(defun-r 'num-val)
;; returns the numeric value of a passed number
;; -OR- zero if the passed variable fails numberp
(defun num-val (pass) (if (numberp pass) pass 0))
(defun-q
vlist->safearray (vertices / arr)
"Returns VERTICES as a safearray
;; convert a list of 2D pline vertices to a vlax-safearray
(defun vlist->safearray (vertices / arr)
VERTICES should be a list of lists of numbers."
(declare (vars (vertices (list nil) (vl-every 'point-p vertices))))
(setq vertices (mapcar 'float (apply 'append vertices))
arr (vlax-make-safearray vlax-vbDouble
(cons 0 (1- (length vertices)))))
(vlax-safearray-fill arr vertices)
arr)
(defun-r 'vlist->safearray)
;; convert an arbitrary-dimension safearray to a list
(defun safearray->list (safearray / dim lst i item return)
(defun-q
safearray->list (safearray / dim lst i item return)
"Converts arbitrary-dimension SAFEARRAY to a list"
(declare (vars (safearray safearray)))
(setq dim (1+ (vlax-safearray-get-dim safearray))
lst (vlax-safearray->list safearray))
(while lst
@ -67,16 +130,13 @@
(add-to-list 'return item)
(setq item nil))
return)
(defun-r 'safearray->list)
;; returns a list of vl-objects which are a member of a vl-collection
;; ex: (collection->list (vla-get-documents (vlax-get-acad-object)))
;; will return the vl-object for each open drawing
(defun collection->list (col-obj / col-lst)
(setq col-lst '())
(vlax-map-collection
col-obj
'(lambda (mem-obj) (append! 'col-lst (list mem-obj)))
) ;_ vlax-map-collection
col-lst
) ;_ defun collection->list
(defun-q
collection->list (collection / ret)
"Returns vlax COLLECTION as a list"
(declare (vars (collection vla-object)))
(vlax-map-collection collection '(lambda (x) (add-to-list 'ret x)))
ret)
(defun-r 'collection->list)

@ -1,5 +1,10 @@
;; get and set custom drawing properties
(defun get-custom-dwgprop (key / si nprops ret si-key si-val)
(set-file-docstring
"Functions dealing with VLA Document objects")
(defun-q
get-custom-dwgprop (key / si nprops ret si-key si-val)
"Returns value of custom drawing property KEY in current acadDoc"
(declare (vars (key str)))
(setq si (vla-get-SummaryInfo acadDoc)
nprops (vla-NumCustomInfo si))
(foreach i (range 0 nprops 1)
@ -7,16 +12,29 @@
(if (= key si-key)
(setq ret si-val)))
ret)
(defun set-custom-dwgprop (key val / si)
(defun-r 'get-custom-dwgprop)
(defun-q
set-custom-dwgprop (key val / si)
"Sets value of custom drawing property KEY to VAL"
(declare (vars (key str)
(val str)))
(setq si (vla-get-SummaryInfo acadDoc))
(if (get-custom-dwgprop key)
(vla-SetCustomByKey si key val)
(vla-AddCustomInfo si key val))
val)
(defun-r 'set-custom-dwgprop)
(defun-q
open-and (filename readonly func-sym args / oldAcadDoc ret)
"Opens drawing FILENAME and executes function pointed to by FUNC-SYM with ARGS
;; open a drawing, change acaddoc to its Document and execute func with args
(defun open-and (filename readonly func-sym args
/ oldAcadDoc ret)
If READONLY is non-nil, open the drawing in read-only mode. The drawing is automatically
closed and %reset-doc is called after."
(declare (vars (filename str)
(func-sym sym (function-p (vl-symbol-value func-sym)))
(args nil (listp args))))
(setq oldAcadDoc acadDoc
acadDoc (vla-Open (vla-get-Documents acadObj)
filename
@ -30,30 +48,32 @@
(vla-Close acadDoc :vlax-false)
(reset-doc)
ret)
(defun-r 'open-and)
;; suppress/allow snaps in current doc by masking bits
(defun osnap-off ()
;; these functions mask bits in osmode to simulate pressing F3
(defun-q
osnap-off ()
"Suppress object snaps in current acadDoc and set *error* to restore them"
(defun *error* (msg)
(osnap-on)
(setq *error* nil))
(setvar 'osmode (logior (expt 2 14)
(getvar 'osmode))))
(defun osnap-on ()
(setvar 'osmode (logand (~ (expt 2 14))
(getvar 'osmode))))
;; ensure necessary layers, linetypes and text styles are present
(defun setup-env (3d-p / textstyles
textstyle-names
linetypes
linetype-names
layers
layer-names
color
idx
req-textstyles
req-linestyles
req-layers)
(vla-setvar 'osmode (logior (expt 2 14)
(vla-getvar 'osmode))))
(defun-r 'osnap-off)
(defun-q
osnap-on ()
"Restore object snaps in current acadDoc"
(vla-setvar 'osmode (logand (~ (expt 2 14))
(vla-getvar 'osmode))))
(defun-r 'osnap-on)
(defun-q
setup-env (3d-p / textstyles textstyle-names linetypes linetype-names layers layer-names
color idx req-textstyles req-linestyles req-layers)
"Ensure necessary layers, linetypes, and text styles are present
Includes different things if 3D-P is non-nil."
(setq linetypes (vla-get-Linetypes acadDoc)
linetype-names (vlax-collection->lst linetypes)
req-linetypes (remove '("CONTINUOUS"
@ -121,27 +141,46 @@
(vla-put-ColorIndex color idx)
(vla-put-TrueColor (vla-Item layers (car l)) color)
(vla-put-Linetype (vla-Item layers (car l)) (caddr l))))
(defun-r 'setup-env)
;; reset acaddoc and modelspace vars in case we errored out in another drawing
(defun reset-doc ()
(defun-q
reset-doc ()
"Resets acadDoc and modelSpace global vars"
(setq acadDoc (vla-get-ActiveDocument acadObj)
modelSpace (vla-get-ModelSpace acadDoc)))
(defun-r 'reset-doc)
;; shortcut to setting active dim style in current doc by name
(defun set-active-dimstyle (name / )
(defun-q
set-active-dimstyle (name / )
"Sets active dimension style in current acadDoc to NAME"
(declare (vars (name str)))
(vla-put-ActiveDimStyle acadDoc (vla-Item (vla-get-DimStyles acadDoc) name)))
(defun-r 'set-active-dimstyle)
;; shortcut to setting active layer in current doc by name
(defun set-active-layer (name / )
(defun-q
set-active-layer (name / )
"Sets active layer in current acadDoc to NAME"
(declare (vars (name str)))
(vla-put-ActiveLayer acadDoc (vla-Item (vla-get-Layers acadDoc) name)))
(defun-r 'set-active-layer)
;; shortcut to setting active text style in current doc by name
(defun set-active-textstyle (name / )
(defun-q
set-active-textstyle (name / )
"Sets active text style in current acadDoc to NAME"
(declare (vars (name str)))
(vla-put-ActiveTextStyle acadDoc (vla-Item (vla-get-TextStyles acadDoc) name)))
(defun-r 'set-active-textstyle)
;; shortcut to system variables in current doc
(defun vla-getvar (varsym / )
(vlax-variant-value (vla-GetVariable acadDoc (to-string varsym))))
(defun vla-setvar (varsym val / )
(vla-SetVariable acadDoc (to-string varsym) (vlax-make-variant val)))
(defun-q
vla-getvar (sysvar / )
"Returns the value of SYSVAR in current acadDoc"
(declare (vars (sysvar (sym str))))
(vlax-variant-value (vla-GetVariable acadDoc (to-string sysvar))))
(defun-r 'vla-getvar)
(defun-q
vla-setvar (sysvar val / )
"Sets the value of SYSVAR in current acadDoc to VAL"
(declare (vars (sysvar (sym str))))
(vla-SetVariable acadDoc (to-string sysvar) (vlax-make-variant val)))
(defun-r 'vla-setvar)

@ -0,0 +1,323 @@
(set-file-docstring
"Functions for documentation build system")
(setq *docstrings-short* nil
*docstrings-full* nil)
(defun-q
doc--add-docstring (func-sym docstring)
"Adds DOCSTRING for FUNCSYM to global docstring vars
Adds the whole DOCSTRING to *docstrings-full* and the first line only to
*docstrings-short*. Both are entered as a cons cell of the form (FUNC-SYM . DOCSTRING)."
(add-to-list '*docstrings-short* (cons func-sym (first-line docstring)))
(add-to-list '*docstrings-full* (cons func-sym docstring)))
(defun-r 'doc--add-docstring)
(defun-q
defun-r (func-sym / func-body func-args func-sig func-docstring func-declares no-func-name)
"Registers the function FUNC-SYM points to
Enables documentation generation and lookups with %whatis and %docstring. Replaces declare
forms in the function body. See %defun-r--process-declare for declare forms.
Redefines function using defun unless *no-redefun* is non-nil.
If *dev-mode* is nil, skip most of the process: only extract docstrings and declare forms,
then redefine the function.
If *inhibit-func-name* is nil, advise the function to set a variable *func-name* with its
name as a string."
(setq func-body (defun-q-list-ref func-sym)
func-args (pop! 'func-body)
func-docstring (pop! 'func-body)
func-declares (if (and (listp (car func-body))
(= (caar func-body) 'declare))
(cdr (pop! 'func-body))))
(if *dev-mode*
(progn
;; declares
(if (not func-declares)
(setq func-declares (cdr (assoc func-sym *delayed-decl*))))
(mapcar '(lambda (decl / docstr-advice body-advice)
(if (atom decl)
(setq decl (list decl)))
(setq docstr-advice ((lambda(/ str)
(setq str (strcat "\n\n" (to-string (car decl))
(if (cdr decl) ":" "")))
(foreach elt (cdr decl)
(setq str (strcat str "\n" (to-string elt))))
str))
func-docstring (strcat func-docstring docstr-advice))
(setq body-advice (defun-r--process-declare decl)
func-body (if body-advice (cons body-advice func-body) func-body)))
func-declares)
;; *func-name*
(if (not no-func-name)
(progn
(if (not (member '/ func-args))
(setq func-args (rcons func-args '/)))
(setq func-args (rcons func-args '*func-name*)
func-body (cons (list 'setq '*func-name* (to-string func-sym)) func-body))))
(doc--add-docstring func-sym func-docstring)
;; signature
(setq func-sig (take-while '(lambda(a) (/= a '/)) func-args))
(add-to-list '*signatures* (cons func-sym
(lowercase
(to-string
(cons func-sym func-sig)))))
(if *doc-build-p*
(add-to-alist '*file-docs*
*file-name*
(list (list
;; name
(lowercase (to-string func-sym))
;; args
(lowercase (to-string func-sig))
;; sig
(apply 'strcat
(append (list "(<span class=\\\"funcname\\\">"
;; HTML escape angle braces in sym
(string-subst-all "&lt;" "<"
(string-subst-all "&gt;" ">"
(lowercase (to-string func-sym))))
"</span>")
(mapcar '(lambda(a) (strcat " "
(lowercase (to-string a))))
func-sig)
'(")")))
;; doc_short
(escape-quotes (first-line func-docstring))
;; doc_full
(apply (compose '(escape-quotes escape-newlines))
(list func-docstring))))
t))))
(if *no-redefun*
(defun-q-list-set func-sym (cons func-args func-body))
(eval (append (list 'defun func-sym func-args) func-body))))
(defun-r 'defun-r)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; the following only needed in *dev-mode* ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(if *dev-mode*
(progn
(defun-q
defun-r--process-declare (declare-form)
"Returns a form for inclusion in the function being processed by %defun-r
Each possible declare form has an expansion into a proper expression form, which is
inserted in place of the declare form in the function. Multiple declare forms are inserted
in the order specified. Declare form processors may modify the args, body, docstring, or
even name of the defining function, but should expressly declare doing so.
To define a new form, define a function named defun-r--process-declare-NAME, where NAME is
the first element of the form, e.g. %defun-r--process-declare-vars is called when the form
is (vars ...). It will be called with a single arg: the cdr of the declare form. It should
return a form to be consed onto the front of the function's body. Processors may return
nil which will not be included in the function body. Put processor functions right after
this one in the file so they load as early as possible.
The entire declare form will be appended to the end of the function's docstring.
See the following for available declares:
- %defun-r--process-declare-with-data
- %defun-r--process-declare-vars
- %defun-r--process-declare-tests"
((symcat '("defun-r--process-declare-" #(to-string (car declare-form))))
(cdr declare-form)))
(defun-r 'defun-r--process-declare)
(defun-q
defun-r--process-declare-with-data (_)
"Returns function advice that ensures this function was called inside a with-data call"
'(assert 'from-with-data))
(defun-r 'defun-r--process-declare-with-data)
(defun-q
defun-r--process-declare-no-func-name (_)
"Tells %defun-r not to advise the function to advertise its name"
(setq no-func-name t)
nil)
(defun-r 'defun-r--process-declare-no-func-name)
(defun-q
defun-r--process-declare-vars (vars-spec / ret)
"Returns function advice that checks vars in the eval environment
VARS-SPEC should be a list of variable specifications for passing to
%defun-r--process-declare-var."
(setq ret (apply 'append
(mapcar 'defun-r--process-declare-var
(subst '(from-with-data non-nil) 'with-data vars-spec))))
(if ret (cons 'progn ret)))
(defun-r 'defun-r--process-declare-vars)
(defun-q
defun-r--process-declare-var (var-spec / var types)
"Processes a single form for %defun-r--process-declare-vars
The car of VAR-SPEC is the name of the variable.
The cadr is a list of types. The type of the var must be one of the types. Specify nil for
the types list to allow any type (but still include validators). To say a var must be nil,
specify types as (nil).
If there are still elements in VAR-SPEC, they are validator forms. Each is evaluated and
if any return nil, throw an error."
(setq var (pop! 'var-spec)
types (pop! 'var-spec))
(if (not (listp types))
(setq types (list types)))
(append (if (member 'non-nil types)
(progn
(remove! 'types '(= x 'non-nil))
(#! '((assert (quote #var))))))
(if types
(#! '((assert '(member (type #var) (quote #types))))))
(mapcar '(lambda(v) (#! '(assert (quote #v)))) var-spec)))
(defun-r 'defun-r--process-declare-var)
(defun-q
defun-r--process-declare-tests (tests / test-sym)
"Adds tests for defining function. Nothing is added to function body
If *test-build-p* is nil, nothing happens. Otherwise, all forms are gathered together into
a test function (see %defun-t) that is automatically eval'd.
This is useful in that it adds expected behavior to docstrings automatically. Tests
specified this way should be short and easy to read -- ideally the function call wrapped
in an =/equal with its expected output. Tests that require setup/teardown should be
defined using %defun-t."
(if *test-build-p*
(progn
(setq test-sym (read (strcat "autotest-" (to-string func-sym))))
(eval (list 'defun-q
test-sym nil
"declare autotest"
(list 'assert-all (list 'quote tests))))
(defun-t test-sym)))
nil)
(defun-r 'defun-r--process-declare-tests)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; documentation system now up and running ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;
;; query functions ;;
;;;;;;;;;;;;;;;;;;;;;
(defun-q
whatis (func-sym)
"Prints the one-line description of the function FUNC-SYM points to
Returns nil if FUNC-SYM wasn't defined using the special documentation system."
(declare (vars (func-sym (sym))))
(doc--print-docstring func-sym "short"))
(defun-r 'whatis)
(defun-q
docstring (func-sym)
"Prints the full docstring of the function FUNC-SYM points to
Returns nil if FUNC-SYM wasn't defined using the special documentation system."
(declare (vars (func-sym (sym))))
(doc--print-docstring func-sym "full"))
(defun-r 'docstring)
(defun-q
doc--print-docstring (func-sym which / dstr)
"Print the docstring for FUNC-SYM from WHICH, which is either \"short\" or \"long\"
Will retrieve docstring from *docstrings-short* or *docstrings-long*, respectively. If
FUNC-SYM isn't valid, return nil."
(setq dstr (cdr (assoc func-sym (symcat '("*docstrings-" #which "*")))))
(if dstr
(progn
(princ (strcat (cdr (assoc func-sym *signatures*)) "\n" dstr))
(princ))))
(defun-r 'doc--print-docstring)
;;;;;;;;;;;;;;;;;;;;;
;; build functions ;;
;;;;;;;;;;;;;;;;;;;;;
(defun-q
doc-build ()
"Bootstraps doc build process and opens a new blank drawing to carry it out"
(propagate '*doc-build-p* t)
(propagate '*test-build-p* t)
(vla-Activate (vla-Add (vla-get-Documents acadObj)))
(princ))
(defun-r 'doc-build)
(defun-q
doc-build-html ()
"Builds HTML docs from files.json (generated by %doc--write-json)"
(doc--write-json)
(princ "\nBuilding HTML files")
(startapp (findfile "docs/code-manual/build/dist/build.exe")
(strcat psc-src-dir "docs/code-manual/")))
(defun-r 'doc-build-html)
(defun-q
doc--write-json (/ f)
"Writes file and function docs to file
All currently generated file and function documentation lives in the global variable
*file-docs*. Format its contents in JSON and write docs/code-manual/files.json."
(princ "\nWriting files.json")
(setq f (open (findfile "docs/code-manual/files.json") "w"))
(princ (doc--json-full-docs) f)
(close f))
(defun-r 'doc--write-json)
(defun-q
doc--json-full-docs ()
"Returns the full documentation as a JSON string"
(strcat "["
(strjoin (mapcar 'doc--json-file-docs
(vl-sort *file-docs* (sort-by 'car '<)))
",")
"]"))
(defun-r 'doc--json-full-docs)
(defun-q
doc--json-file-docs (file-entry / file-name)
"Returns file documentation as a JSON string"
(setq file-name (pop! 'file-entry))
(json-object
(list (json-string-prop "name" file-name)
(json-string-prop "doc_short" (pop! 'file-entry))
(json-string-prop "doc_full" (apply (compose '(escape-newlines escape-quotes))
(list (pop! 'file-entry))))
(json-array-prop "functions" (mapcar 'doc--json-func-docs
(vl-sort file-entry (sort-by 'car '<)))))))
(defun-r 'doc--json-file-docs)
(defun-q
doc--json-func-docs (func-entry / name priv-p)
"Returns function documentation as a JSON string"
(declare (vars (func-entry list)
(file-name str)))
(setq name (pop! 'func-entry)
priv-p (wcmatch name "*--*"))
(json-object
(list (json-string-prop "file" file-name)
(json-string-prop "name" name)
(json-string-prop "access" (if priv-p "priv" "pub"))
(json-string-prop "args" (pop! 'func-entry))
(json-string-prop "sig" (pop! 'func-entry))
(json-string-prop "doc_short" (pop! 'func-entry))
(json-string-prop "doc_full" (pop! 'func-entry)))))
(defun-r 'doc--json-func-docs)))
;; end of *dev-mode* stuff
;; process functions defined earlier
;; see beginning of acaddoc.lsp for early defun-r definition
(map-apply '(lambda (*file-name* f-sym) (defun-r f-sym))
*delayed-doc*)
(setq *delayed-doc* nil)

@ -1,35 +1,94 @@
;; print debug messages when debug-flag is T
;; msgs is a list of things that get combined with strcat and to-string
;; leading newline is added automatically
(defun debug-print (msgs)
(if debug-flag
(set-file-docstring
"Error-handling functions")
(defun-q
debug-print (msgs)
"Prints debug messages when *dev-mode* is non-nil
MSGS is a list of things that will be %macro-expand'd, turned into strings, concatenated,
and printed to console. A newline will be added to the front of the resulting string
automatically."
(if *dev-mode*
(princ (apply 'strcat (cons "\n" (mapcar 'to-string (macro-expand msgs))))))
(princ))
(defun-r 'debug-print)
(defun-q
debug-print-vars (vars)
"Uses %debug-print to print symbol values
VARS should be a list of symbols. Example use:
(setq *dev-mode* t a 1 b 2)
(debug-print-vars '(a b))
prints
;; use debug-print to show variables
(defun debug-print-vars (vars)
A: 1
B: 2"
(declare no-func-name)
(debug-print (list (strjoin
(mapcar '(lambda(x)
(strcat (vl-symbol-name x) ": " (to-string (vl-symbol-value x))))
vars)
"\n")
"\n")))
(defun-r 'debug-print-vars)
;; print message and exit
(defun error (msg / )
(defun-q
error (msg / )
"Calls *error* if defined, prints MSG, and exits
If *error-prefix* is defined, it is used as the message prefix. Otherwise, the default
[Error] is used."
(setq *last-error* msg)
(if *error* (*error* ""))
(*push-error-using-stack*)
(defun *error* (x)
(if (not no-print-errors)
(vla-Prompt (vla-get-Utility acadDoc) (strcat "\n[Error] " msg "\n")))
(defun *error* (x / prefix)
(setq prefix (?? *error-prefix* "[Error]"))
(if (not *no-print-errors*)
(vla-Prompt (vla-get-Utility acadDoc)
(strcat "\n" prefix " " msg "\n")))
(setq *error* nil))
(exit))
(defun-r 'error)
(defun-q
catch-all-error (attempt)
"Wrapper for vl-catch-all-error-message
;; wrapper for vl-catch-all-error-message that uses our custom error
;; message if we threw the error
(defun catch-all-error (attempt)
This function returns *last-error* as the error message if the error was a quit/exit (which should indicate we threw it using %error)."
(if (= (vl-catch-all-error-message attempt)
"quit / exit abort")
*last-error*
(vl-catch-all-error-message attempt)))
(defun-r 'catch-all-error)
(defun-q
assert (expr / val)
"Throws an %error if EXPR evaluates to nil
If *assert-return-error* is non-nil, return the error string rather than
throwing. Otherwise, returns the value of EXPR."
(if (setq val (eval expr))
(if *assert-return-error* nil val)
(if *assert-return-error*
(strcat "[Assert failed] " (to-string expr))
((lambda(*error-prefix*)
(error (to-string expr)))
(if *func-name*
(strcat "[Assert failed (" *func-name* ")]")
"[Assert failed]")))))
(declare-late 'assert '(no-func-name))
(defun-r 'assert)
(defun-q
assert-all (expr-lst)
"%assert all expressions in EXPR-LST. If no errors, return a list of results
If *ASSERT-RETURN-ERROR* is non-nil, catch the errors and return them as a list. If there
were no errors in this case, return nil."
(strip-nil
(mapcar 'assert expr-lst)))
(declare-late 'assert-all '(no-func-name))
(defun-r 'assert-all)

@ -1,23 +1,28 @@
;; ask the user to select files from this directory using a dialog
(defun get-files-this-dir (full-path
/
;; for dialog-init
defaults-file
popup-keys
dist-keys
int-keys
toggle-keys
plain-string-keys
default-actions
dialog-ties
sub-dialogs
extra-keys
tile-actions
dialog-save-action
;; local
files
path
)
(set-file-docstring
"File-handling functions")
(defun-q
get-files-this-dir (full-path /
;; for dialog-init
defaults-file
popup-keys
dist-keys
int-keys
toggle-keys
plain-string-keys
default-actions
dialog-ties
sub-dialogs
extra-keys
tile-actions
dialog-save-action
;; local
files
path
)
"Prompts to select files from current directory using dialog listbox
The DCL for this dialog is files-this-dir.dcl."
(setq files (vl-sort (vl-directory-files (vla-get-Path acadDoc) "*.dwg" 1) '<)
popup-keys '(("files" . files))
path (vla-get-path acaddoc))
@ -32,10 +37,12 @@
(if full-path
(mapcar '(lambda(x) (strcat path "/" x)) files)
files))
(defun-r 'get-files-this-dir)
;; read file to a string
(defun read-file-to-string (file / line ret)
(chkargs "read-file-to-string" '((file str)))
(defun-q
read-file-to-string (file / line ret)
"Returns the contents of FILE as a string"
(declare (vars (file str)))
(setq file (findfile file))
(if file
(progn
@ -44,15 +51,20 @@
(add-to-list 'ret line))
(close file)))
(strjoin ret "\n"))
(defun-r 'read-file-to-string)
;; open a CSV with Excel
(defun open-csv (file)
(defun-q
open-csv (file)
"Opens FILE with Excel"
(declare (vars (file str)))
(startapp "C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE"
(strcat "\"" file "\"")))
(defun-r 'open-csv)
;; read the contents of a CSV file into a list
(defun read-csv (file / line ret)
(chkargs "read-csv" '((file str)))
(defun-q
read-csv (file / line ret)
"Return the contents of csv FILE as a list of lists of strings"
(declare (vars (file str)))
(setq file (findfile file))
(if file
(progn
@ -61,12 +73,16 @@
(add-to-list 'ret (strsplit line ",")))
(close file)))
ret)
(defun-r 'read-csv)
(defun-q
write-csv (lines file wrap-str / line)
"Write the contents of LINES to FILE
;; write the contents of a 2D list to a csv
;; if wrap-str is true, wrap each cell in quotes
(defun write-csv (lines file wrap-str / line)
(chkargs "write-csv" '((lines list)
(file str)))
LINES should be a list of lists of strings. FILE should be a file name as a string. If
WRAP-STR is non-nil, wrap each cell in a formula like =\"CELL-CONTENT\"."
(declare (vars (lines list)
(file str)))
(setq file (open file "w"))
(foreach line lines
(setq line (strjoin (mapcar '(lambda(s)
@ -78,10 +94,12 @@
","))
(write-line line file))
(close file))
(defun-r 'write-csv)
;; parse the job info file (or any other defaults file)
(defun read-key-val-file (file / line data)
(chkargs "read-key-val-file" '((file str)))
(defun-q
read-key-val-file (file / line data)
"Return the contents of key/value FILE as a list of 2-element lists"
(declare (vars (file str)))
(if (setq file (open file "r"))
(progn
(while (setq line (read-line file))
@ -89,9 +107,20 @@
(add-to-list 'data (list (car line) (cadr line))))
(close file)))
data)
(defun-r 'read-key-val-file)
;; retrieve unique primary key for def in repo-file
(defun process-generic-def (def repo-file / defs filename current found-match-p)
(defun-q
process-generic-def (def repo-file / defs filename current found-match-p)
"Retrieves the unique primary key for DEF in REPO-FILE
DEF should be the definition of an object as a list. REPO-FILE should be the name of a csv
file that serves as the database of that object type. Each unique object definition is
assigned a primary key number in the database. This function returns DEF with the primary
key as the first item.
This function handles creating REPO-FILE and assigning primary keys."
(declare (vars (def list)
(repo-file str)))
(setq def (mapcar 'to-string def)
filename repo-file
defs (read-csv filename)
@ -117,4 +146,4 @@
(write-line (strjoin def ",") repo-file)
(close repo-file)))
def)
(defun-r 'process-generic-def)

@ -1,25 +1,71 @@
;; return T if x is a function
(defun function-p (x)
(set-file-docstring
"Function-handling functions")
(defun-q
function-p (x)
"Returns nil unless X is a function"
(declare (tests (function-p +)
(not (function-p '(lambda() 1)))
(not (function-p 123))))
(member (type x) '(SUBR USUBR)))
(defun-r 'function-p)
(defun-q
function-or-lambda-p (x / xt)
"Returns nil unless X is a function or a lambda"
(declare (tests (function-or-lambda-p +)
(function-or-lambda-p '(lambda() 1))
(not (function-or-lambda-p 123))))
(setq xt (type x))
(cond ((function-p x) t)
((= xt 'list)
(and (= (car x) 'lambda)
(listp (cadr x))
(> (length x) 2)))
((= xt 'sym) (function-p (vl-symbol-value x)))))
(defun-r 'function-or-lambda-p)
;; if func is a function, apply it to args
(defun attempt-apply (func args)
(defun-q
attempt-apply (func args)
"If FUNC is a function, returns the result of applying it to ARGS. Else, return nil"
(declare (vars (func) (args (list nil))))
(if (= (type func) 'SYM)
(setq func (vl-symbol-value func)))
(if (and (function-p func)
(member (type args) '(LIST nil)))
(apply 'func args)))
(defun-r 'attempt-apply)
(defun-q
compose (funcs-lst)
"Returns a lambda that is the result of composing functions in FUNCS-LST
;; return a lambda that is the result of composing multiple single-arg functions
(defun compose (funcs-lst)
FUNCS-LST should be a list of symbols pointing to single-argument functions. The resulting
function ill take a single argument and return the result of applying all the functions in
FUNCS-LST in the reverse of the order specified.
Example:
(compose '(lowercase to-string)) returns a function that take an argument and
returns the result of applying %to-string to it, followed by %lowercase."
(if (null funcs-lst)
'(lambda(x) x)
(macro-expand '(lambda(x) (#(car funcs-lst) (#(compose (cdr funcs-lst)) x))))))
'(lambda(itself) itself)
(macro-expand '(lambda(compose-x)
(#(car funcs-lst) (#(compose (cdr funcs-lst)) compose-x))))))
(defun-r 'compose)
(defun-q
apply-by-twos (func lst / arg1 arg2 ret)
"Applies FUNC to adjacent elements of LST, accumulates and returns the results.
;; apply func to the first and second items in lst, then the second
;; and third, and so on
;; accumulate and return results
(defun apply-by-twos (func lst / arg1 arg2 ret)
Example:
(apply-by-twos '+ '(1 2 3)) => '(3 5)
which is equivalently shown as
(list (+ 1 2) (+ 2 3))"
(declare (vars (func nil (function-or-lambda-p func))
(lst nil (listp lst)))
(tests (equal (apply-by-twos '+ '(1 2 3)) '(3 5))))
(setq arg1 (car lst)
arg2 (cadr lst)
lst (cddr lst))
@ -29,13 +75,26 @@
arg2 (car lst)
lst (cdr lst)))
ret)
(defun-r 'apply-by-twos)
;; apply f to each element of map and return the results
(defun map-apply (f map)
(defun-q
map-apply (f map)
"Returns the results of applying F to each element of MAP
MAP should be a list of lists of arguments to F."
(mapcar '(lambda(f args) (apply f args))
(lst* f (length map))
map))
;; same as above but in place
(defun map-apply! (f map-sym)
(set map-sym (map-apply f (vl-symbol-value map-sym)))
) ;_ defun map-apply!
(declare-late 'map-apply
'((vars (f nil (function-or-lambda-p f))
(map nil (listp map)))
(tests (equal (map-apply '+ '((1 2) (3 4))) '(3 7)))))
(defun-r 'map-apply)
(defun-q
map-apply! (f map-sym)
"Updates the list at MAP-SYM in place using %map-apply"
(declare (vars (f nil (function-or-lambda-p f))
(map-sym sym (listp (vl-symbol-value map-sym)))))
(set map-sym (map-apply f (vl-symbol-value map-sym))))
(defun-r 'map-apply!)

@ -1,14 +1,23 @@
;; return a list of segment lengths for pipe bending
;; holds the minimum starting segment at 6" and the ending segment at 18"
(defun calc-segs (pts / ret)
(set-file-docstring
"Functions related to points, vectors, and planes")
(defun-q
calc-segs (pts / ret)
"Returns a list of segment lengths between points in PTS
Holds the minimum starting segment at 6\" and the ending segment at 18\" for pipe
bending."
(declare (vars (pts nil (listp pts) (vl-every 'point-p pts))))
(add-to-list 'ret (max 6 (distance (nth 0 pts) (nth 1 pts))))
(foreach i (range 1 (- (length pts) 2) 1)
(add-to-list 'ret (distance (nth i pts) (nth (1+ i) pts))))
(add-to-list 'ret (max 18 (distance (circular-nth -2 pts)
(circular-nth -1 pts)))))
(defun-r 'calc-segs)
;; return a list of internal angles between pts
(defun calc-angles (pts / a b c ang normal ret)
(defun-q
calc-angles (pts / a b c ang normal ret)
"Returns a list of internal angles between PTS"
(while (> (length pts) 2)
(setq ang (take 3 pts)
normal (apply 'normal-vec ang)
@ -16,9 +25,11 @@
(pop! 'pts)
(add-to-list 'ret (- 180 ang)))
ret)
(defun-r 'calc-angles)
;; calculate dihedral angle between two planes defined by their normal vectors
(defun dihedral (v1 v2 / ax ay az bx by bz)
(defun-q
dihedral (v1 v2 / ax ay az bx by bz)
"Returns the dihedral angle between two planes defined as normal vectors"
(chkargs "dihedral" '((v1 list ((vl-every 'numberp v1)
(= (length v1) 3)))
(v2 list ((vl-every 'numberp v2)
@ -32,13 +43,17 @@
(acos (/ (abs (+ (* ax bx) (* ay by) (* az bz)))
(* (sqrt (apply '+ (mapcar 'square (list ax ay az))))
(sqrt (apply '+ (mapcar 'square (list bx by bz))))))))
(defun-r 'dihedral)
;; return the 3D vector normal to the points
(defun normal-vec (pt1 pt2 pt3)
(defun-q
normal-vec (pt1 pt2 pt3)
"Returns the 3D vector normal to 3 points"
(cross-product (vec- pt2 pt1) (vec- pt2 pt3)))
(defun-r 'normal-vec)
;; 3D vector subtraction
(defun vec- (v1 v2)
(defun-q
vec- (v1 v2)
"Returns the result of subtracting vector V1 from V2"
(chkargs "vec-" '((v1 list ((vl-every 'numberp v1)
(= (length v1) 3)))
(v2 list ((vl-every 'numberp v2)
@ -46,9 +61,11 @@
(list (- (nth 0 v1) (nth 0 v2))
(- (nth 1 v1) (nth 1 v2))
(- (nth 2 v1) (nth 2 v2))))
(defun-r 'vec-)
;; 3D vector cross product
(defun cross-product (v1 v2 / ax ay az bx by bz)
(defun-q
cross-product (v1 v2 / ax ay az bx by bz)
"Returns the cross product of vectors V1 and V2"
(chkargs "cross-product" '((v1 list ((vl-every 'numberp v1)
(= (length v1) 3)))
(v2 list ((vl-every 'numberp v2)
@ -62,10 +79,14 @@
(list (- (* ay bz) (* az by))
(- (* az bx) (* ax bz))
(- (* ax by) (* ay bx))))
(defun-r 'cross-product)
(defun-q
pipe-bend-lines (segments-lst / segment ret radius k)
"Returns the bend lines for a PSC pipe with segments SEGMENTS-LST
;; return the bend lines for a 1.66OD pipe w/ CL radius of 2.83 and k=0.18
;; elements are strings
(defun pipe-bend-lines (segments-lst / segment ret radius k)
Each element to SEGMENTS-LST should be a string. Assumes 1.66OD pipe, centerline radius of
2.83\" and k value of 0.18."
(setq radius 2.83
k 0.18)
(if (/= (rem (length segments-lst) 2) 0)
@ -110,15 +131,16 @@
(mapcar '(lambda(x) (list (frac (value "mark" x))
(rtos (value "ang" x) 2 1)))
ret))))))
;; check if a point is within a box defined by two other points
(defun within-box-p (pt boxpt1 boxpt2 / ptx ptx b1x b1y b2x b2y lx rx ty by)
(chkargs "within-box-p" '((pt list ((>= (length pt) 2)
(vl-every 'numberp pt)))
(boxpt1 list ((>= (length boxpt1) 2)
(vl-every 'numberp boxpt1)))
(boxpt2 list ((>= (length boxpt2) 2)
(vl-every 'numberp boxpt2)))))
(defun-r 'pipe-bend-lines)
(defun-q
within-box-p (pt boxpt1 boxpt2 / ptx ptx b1x b1y b2x b2y lx rx ty by)
"Returns T if PT is within the box defined by BOXPT1 and BOXPT2"
(declare (vars (pt list (point-p pt))
(boxpt1 list (point-p boxpt1))
(boxpt2 list (point-p boxpt2)))
(tests (within-box-p '(0 0) '(-1 -1) '(1 1))
(not (within-box-p '(10 10) '(0 0) '(1 1)))))
(setq ptx (car pt)
pty (cadr pt)
b1x (car boxpt1)
@ -133,74 +155,108 @@
(<= ptx rx)
(>= pty by)
(<= pty ty)))
;; return true if pt is a 2 or 3 item list of numbers
(defun point-p (pt)
(and
(= (type pt) 'list)
(member (length pt) '(2 3))
(vl-every 'numberp pt)
) ;_ and
) ;_ defun
;; return true if every element of pts-list passes point-p
(defun pts-list-p (pts-list)
(and
(= (type pts-list) 'LIST)
(vl-every 'point-p pts-list)))
;; returns a 2D point which is midway between two passed points
(defun mid-2-pts (p1 p2)
(mapcar '+ p1 (mapcar '* '(0.5 0.5 1.0) (mapcar '- p2 p1)))
) ;_ defun mid-2-pts
;; internal angle of 3 points
(defun int-ang (p1 p2 p3 / ret)
(defun-r 'within-box-p)
(defun-q
point-p (pt)
"Returns T if PT is a 2- or 3-item list of numbers"
(and (= (type pt) 'list)
(member (length pt) '(2 3))
(vl-every 'numberp pt)))
(defun-r 'point-p)
(defun-q
pts-list-p (pts-list)
"Returns T if PTS-LIST is a list of points"
(and (listp pts-list)
(vl-every 'point-p pts-list)))
(defun-r 'pts-list-p)
(defun-q
mid-2-pts (p1 p2)
"Returns a 2D point which is midway between P1 and P2"
(mapcar '+ p1 (mapcar '* '(0.5 0.5 1.0) (mapcar '- p2 p1))))
(defun-r 'mid-2-pts)
(defun-q
int-ang (p1 p2 p3 / ret)
"Return the internal angle of 3 points in radians"
(abs (angle-between (angle p2 p1) (angle p2 p3))))
(defun-r 'int-ang)
;; calculate a bend deduction
;; ang is in degrees and represents the excluded angle
(defun bend-deduct (thick ang rad k)
(defun-q
bend-deduct (thick ang rad k)
"Calculates a bend deduction
THICK is the material thickness. ANG is the excluded bend angle in degrees. RAD is the
bend radius. K is the K-value."
(- (* 2 (tan (/ (dtr ang) 2.0)) (+ rad thick))
(dtr (* ang (+ rad (* thick k))))))
;; return the points list for a rectangle
(defun rect-pts (width height ins)
(defun-r 'bend-deduct)
(defun-q
rect-pts (width height ins)
"Returns the points list for a rectangle located at point INS"
(declare (vars (width nil (numberp width))
(height nil (numberp height))
(ins list (point-p ins)))
(tests (equal (rect-pts 1 1 '(0 0))
'((0 0) (1 0) (1 1) (0 1)))
(equal (rect-pts 1 2 '(3 4))
'((3 4) (4 4) (4 6) (3 6)))))
(displace-pts (list '(0 0 0)
(list width 0 0)
(list width height 0)
(list 0 height 0))
ins))
;; mirror a points list over a vertical line at offset x
(defun mirror-pts-v (pts x)
(defun-r 'rect-pts)
(defun-q
mirror-pts-v (pts x)
"Returns points list PTS mirrored over vertical line at X"
(declare (vars (pts list (vl-every 'point-p pts))
(x nil (numberp x)))
(tests (equal (mirror-pts-v '((0 0) (1 1)) 0)
'((0 0) (-1 1)))))
(mapcar '(lambda(pt) (cons (+ x (- x (car pt)))
(cdr pt)))
pts))
;; mirror a points list over a horizontal line at offset y
(defun mirror-pts-h (pts y)
pts))
(defun-r 'mirror-pts-v)
(defun-q
mirror-pts-h (pts y)
"Returns points list PTS mirrored over horizontal line at Y"
(declare (vars (pts list (vl-every 'point-p pts))
(y nil (numberp y)))
(tests (equal (mirror-pts-h '((0 0) (1 1)) 0)
'((0 0) (1 -1)))))
(mapcar '(lambda(pt) (append (list (car pt)
(+ y (- y (cadr pt))))
(cddr pt)))
pts))
pts))
(defun-r 'mirror-pts-h)
;; add third term to two-item point lists
(defun add-bulges (pts)
(defun-q
add-bulges (pts)
"Returns point list PTS with all points padded to 3D"
(mapcar '(lambda(pt)
(if (= (length pt) 2)
(append pt (list 0))
pt))
pts))
(defun-r 'add-bulges)
;; return proper bulge value for a given angle (in radians)
(defun calc-bulge (ang)
(defun-q
calc-bulge (ang)
"Returns the bulge value corresponding to a fillet of ANG radians"
(setq ang (/ ang 4.0))
(/ (sin ang)
(cos ang)))
(defun-r 'calc-bulge)
;; return the angle between two angles (radians)
(defun angle-between (a1 a2 / diff)
(defun-q
angle-between (a1 a2 / diff)
"Returns the angle between angles A1 and A2 (radians)"
(setq a1 (rtd a1)
a2 (rtd a2)
diff (- a1 a2))
@ -212,24 +268,32 @@
(- 360 (- a2 a1)))
(T diff))))
;; fillet an angle defined by 3 2d points (x y)
;; returns a 4-item vlist of format (x y bulge)
(defun calc-fillet (pt1
pt2
pt3
radius
/
l1-angle
l1-len
l2-angle
l2-len
pull-back
int-angle
arc
chord
pull
)
(defun-r 'angle-between)
(defun-q
calc-fillet (pt1
pt2
pt3
radius
/
l1-angle
l1-len
l2-angle
l2-len
pull-back
int-angle
arc
chord
pull
)
"Calculates a fillet for three 2D points at RADIUS
All points should be 2D. The return value will be a list of four 3D points, with the Z
coordinate representing the bulge."
(declare (vars (pt1 list (point-p pt1))
(pt2 list (point-p pt2))
(pt3 list (point-p pt3))
(radius nil (numberp radius))))
(defun pull-back (pt1 pt2 dist / a)
(setq a (angle pt1 pt2))
@ -267,10 +331,13 @@
(append (pull-back pt3 pt2 pull)
'(0))
(append pt3 '(0))))
(defun-r 'calc-fillet)
;; fillet all corners in a list of points
;; assumes 3 points minimum
(defun fillet-all-pts (pts radius / i ncar ncdr f-pts)
(defun-q
fillet-all-pts (pts radius / i ncar ncdr f-pts)
"Returns points list PTS with all corners filleted to RADIUS
See %calc-fillet for individual fillet calculations."
(defun ncar (n lst / i return)
(setq i 0)
(while (< i n)
@ -296,45 +363,12 @@
(ncdr (+ i 3) pts))
i (+ i 2)))
pts)
(defun-r 'fillet-all-pts)
;; simpler than using vla-Mirror and deleting the original
(defun vla-mirror-y (obj)
(vla-TransformBy obj
(vlax-tmatrix '((-1 0 0 0)
(0 1 0 0)
(0 0 1 0)
(0 0 0 1)))))
(defun vla-mirror-x (obj)
(vla-TransformBy obj
(vlax-tmatrix '((1 0 0 0)
(0 -1 0 0)
(0 0 1 0)
(0 0 0 1)))))
(defun mirror&delete (obj pt axis / ret)
(chkargs "mirror&delete"
'(
(obj vla-object)
(pt list (point-p))
(axis sym ((member axis '(x y))))
) ;_ '
) ;_ chkargs
(setq ret
(vla-Mirror
obj
(3dpt pt)
(3dpt
(mapcar '+ pt
(if (= axis 'x) '(1 0) '(0 1))
) ;_ mapcar
) ;_ 3dpt
) ;_ vla-Mirror
) ;_ setq
(vla-delete obj)
ret
) ;_ defun mirror&delete
(defun norm_3pts (org xdir ydir / norm)
(defun-q
norm_3pts (org xdir ydir / norm)
"Returns a normal vector based on an origin x vector and y vector"
(declare (vars (org list point-p) (xdir list point-p) (ydir list point-p)))
(foreach v '(xdir ydir) (set v (mapcar '- (eval v) org)))
(if (inters org xdir org ydir)
(mapcar '(lambda (x) (/ x (distance '(0 0 0) norm)))
@ -342,24 +376,31 @@
(list
(- (* (cadr xdir) (caddr ydir)) (* (caddr xdir) (cadr ydir)))
(- (* (caddr xdir) (car ydir)) (* (car xdir) (caddr ydir)))
(- (* (car xdir) (cadr ydir)) (* (cadr xdir) (car ydir)))
) ;_ list
) ;_ setq
) ;_ mapcar
) ;_ if
) ;_ defun norm_3pts
(defun safe-calc-fillet-3d (pts-lst rad / rad ret)
(- (* (car xdir) (cadr ydir)) (* (cadr xdir) (car ydir))))))))
(defun-r 'norm_3pts)
(defun-q
safe-calc-fillet-3d (pts-lst rad / rad ret *no-print-errors*)
"Repeats %calc-fillet-3d with reduced radius until no error is returned"
(declare
(vars
(pts-lst list (vl-every 'point-p pts-lst) (vl-every 'cddr pts-lst))
(rad nil (numberp rad))
) ;_ vars
) ;_ declare
(setq *no-print-errors* t)
(while
(vl-catch-all-error-p
(setq ret (vl-catch-all-apply 'calc-fillet-3d (list pts-lst rad)))
) ;_ vl-catch-all-error-p
(setq rad (- rad 0.0625))
) ;_ while
ret
) ;_ defun safe-calc-fillet-3d
ret)
(defun-r 'safe-calc-fillet-3d)
(defun calc-fillet-3d (pts-lst rad / xyb-add-z xdir org ydir nor f-ret pts-ret)
(defun-q
calc-fillet-3d (pts-lst rad / xyb-add-z xdir org ydir nor f-ret pts-ret)
"Returns a definition list formatted for use with %draw-fillet-3d access with %safe-calc-fillet-3d"
(defun xyb-add-z (xyb)
(mapcar
'(lambda (xyb)
@ -415,6 +456,94 @@
) ;_ cons
) ;_ setq
) ;_ while
pts-ret
) ;_ defun calc-fillet-3d
pts-ret)
(defun-r 'calc-fillet-3d)
(defun-q
rezero-pts (pts v / delta dx dy)
"Returns points list PTS recentered on the Vth vertex
Rotates the list around until V is the 0th item and displaces the points so they are
correct. Adds Z coordinates if 2D."
(declare (vars (pts nil (pts-list-p pts))
(v int (>= v 0) (<= v (length pts))))
(tests (equal (rezero-pts '((0 0) (1 1)) 1) '((0 0 0) (-1 -1 0)))))
(setq pts (if (= (length (car pts)) 2) (add-bulges pts) pts)
delta (nth v pts)
dx (car delta)
dy (cadr delta))
(circular-shift
(mapcar '(lambda(pt) (list
(- (car pt) dx)
(- (cadr pt) dy)
(caddr pt)))
pts)
v))
(defun-r 'rezero-pts)
(defun-q
displace-pt (pt d)
"Returns point PT displaced by point D"
(declare (vars (pt nil (point-p pt))
(d nil (point-p d)))
(tests (equal (displace-pt '(0 0) '(2 2)) '(2 2))
(equal (displace-pt '(1 2) '(3 4)) '(4 6))))
(mapcar '+ pt d))
(defun-r 'displace-pt)
(defun-q
displace-pts (pts d)
"Returns points list PTS with all points displaced by point D"
(declare (vars (pts nil (pts-list-p pts))
(d nil (point-p d)))
(tests (equal (displace-pts '((0 0) (1 1)) '(2 2)) '((2 2) (3 3)))))
(mapcar '(lambda(x) (mapcar '+ x d)) pts))
(defun-r 'displace-pts)
(defun-q
rotate-pt (pt ang / x y)
"Returns point PT rotated about the origin by ANG (radians)
Adds Z coordinate if 2D."
(declare (vars (pt nil (point-p pt))
(ang nil (numberp ang))))
(setq x (car pt)
y (cadr pt))
(list (- (* x (cos ang)) (* y (sin ang)))
(+ (* y (cos ang)) (* x (sin ang)))
(?f (caddr pt))))
(defun-r 'rotate-pt)
(defun-q
rotate-pts (pts ang)
"Return points list PTS with all points rotated about the orgin by ANG (radians)
Adds Z coordinates if 2D."
(mapcar '(lambda(x) (rotate-pt x ang)) pts))
(defun-r 'rotate-pts)
(defun-q
join-vertices (lines / tmp ret)
"Joins a list of lines
LINES should be a list of lines (lists of points). If the last point of one line equals the
first point of the next, they are joined into one. Otherwise, they are left separate."
(declare (vars (lines nil (listp lines)
(vl-every '(lambda(line) (vl-every 'point-p line))
lines)))
(tests (equal (join-vertices '(((0 0) (1 0))
((1 0) (1 1))
((1 2) (0 2))))
'(((0 0) (1 0) (1 1))
((1 2) (0 2))))))
(while lines
(setq tmp (pop! 'lines))
(if (equal (car tmp) (last (last ret)))
(subst! (append (last ret) (list (cadr tmp))) (last ret) 'ret)
(append! 'ret (list tmp))))
(if (and (equal (caar ret) (last (last ret))) (> (length ret) 1))
(progn
(setq tmp (cdar ret) ret (cdr ret))
(subst! (append (last ret) tmp) (last ret) 'ret)))
ret)
(defun-r 'join-vertices)

@ -1,14 +1,20 @@
;; find the root job directory, if we are a descendant of one
(defun find-job-dir ( / path)
(set-file-docstring
"Functions related to the job directory")
(defun-q
find-job-dir ( / path)
"Returns the root job directory if we are a descendant of one"
(setq path (reverse (strsplit (vla-get-Path acadDoc) "\\")))
(while (and path
(not (or (wcmatch (car path) "#### - *")
(wcmatch (car path) "Job####"))))
(setq path (cdr path)))
(if path (strjoin (reverse path) "\\")))
(defun-r 'find-job-dir)
;; return the job-info data list
(defun get-job-info (/ job-dir job-info)
(defun-q
get-job-info (/ job-dir job-info)
"Return the current job info as a data list"
(if (setq job-dir (find-job-dir))
(if (member "job.dat" (vl-directory-files job-dir))
(setq job-info (read-key-val-file (strcat job-dir "/job.dat")))
@ -16,3 +22,4 @@
(error "FAB: Not in a job folder"))
(cons (list "dir" job-dir)
job-info))
(defun-r 'get-job-info)

@ -0,0 +1,38 @@
(set-file-docstring
"Functions for creating JSON lists
See the following for usage:
- %doc--json-full-docs
- %doc--json-file-docs
- %doc--json-func-docs")
(defun-q
json-prop (key value)
"Return KEY and VALUE as a JSON property"
(declare (vars (key str)
(value str)))
(strcat "\"" key "\"" ":" value))
(defun-r 'json-prop)
(defun-q
json-string-prop (key value)
"Return KEY and VALUE as a JSON string property"
(declare (vars (key str)
(value str)))
(json-prop key (strcat "\"" value "\"")))
(defun-r 'json-string-prop)
(defun-q
json-array-prop (key value)
"Return KEY and VALUE as a JSON array property"
(declare (vars (key str)
(value list (vl-every 'stringp value))))
(json-prop key (strcat "[" (strjoin value ",") "]")))
(defun-r 'json-array-prop)
(defun-q
json-object (props)
"Combine the JSON properties in PROPS into an object and return it"
(declare (vars (props list (vl-every 'stringp props))))
(strcat "{" (strjoin props ",") "}"))
(defun-r 'json-object)

@ -1,25 +1,78 @@
;; remove the car from lst-sym and return it
(defun pop! (pop-lst-sym / pop-lst ret)
(set-file-docstring
"List-handling functions")
(defun-q
pop! (pop-lst-sym / pop-lst ret)
"Sets LST-SYM to (cdr LST) and returns (car LST)"
(setq pop-lst (vl-symbol-value pop-lst-sym)
ret (car pop-lst))
(set pop-lst-sym (cdr pop-lst))
ret)
;; cons to the end of the list
(defun rcons (lst n)
(append lst (list n)))
;; return the first n elements from lst
(defun take (n lst / ret)
(declare-late 'pop!
'((vars (pop-lst-sym sym))
(tests
(equal ((lambda(lst) (pop! 'lst)) nil) nil)
(equal ((lambda(lst) (pop! 'lst)) '(1)) 1)
(equal ((lambda(lst) (pop! 'lst) lst) '(1 2)) '(2)))))
(defun-r 'pop!)
(defun-q
rcons (lst x)
"Returns LST with X added to the end"
(append lst (list x)))
(declare-late 'rcons
'((vars
(lst nil (listp lst)))
(tests
(equal (rcons '(1) 2) '(1 2))
(equal (rcons nil 1) '(1)))))
(defun-r 'rcons)
(defun-q
take (n lst / ret)
"Returns the first N elements of LST"
(setq n (min n (length lst)))
(while (> n 0)
(add-to-list 'ret (car lst))
(setq lst (cdr lst))
(dec! 'n))
ret)
;; return the first n elements from lst, removing them
(defun take! (n take-lst-sym / take-lst ret)
(declare-late 'take
'((vars (n int) (lst nil (listp lst)))
(tests
(equal (take 1 '(1 2)) '(1))
(equal (take 2 '(1)) '(1))
(= (take 1 nil) nil))))
(defun-r 'take)
(defun-q
take-while (predicate lst / x ret)
"Returns the first elements of LST for which PREDICATE is true
PREDICATE should be a function that takes one element."
(while lst
(setq x (car lst)
lst (cdr lst))
(if (apply predicate (list x))
(add-to-list 'ret x)
(setq lst nil)))
ret)
(declare-late 'take-while
'((vars (predicate (list sym) (if (listp predicate)
(= (car predicate) 'lambda)
(function-p (vl-symbol-value predicate))))
(lst (list nil) (listp lst)))
(tests (equal (take-while 'numberp '(1 2 nil 3)) '(1 2)))))
(defun-r 'take-while)
(defun-q
take! (n take-lst-sym / take-lst ret)
"Returns the first N elements from TAKE-LST-SYM, removing them"
(declare (vars (n (int))
(take-lst-sym sym (listp (vl-symbol-value take-lst-sym))))
(tests
(equal ((lambda(lst) (take! 1 'lst)) '(1 2)) '(1))
(equal ((lambda(lst) (take! 1 'lst) lst) '(1 2)) '(2))))
(setq take-lst (vl-symbol-value take-lst-sym)
n (min n (length take-lst)))
(while (> n 0)
@ -28,28 +81,31 @@
(dec! 'n))
(set take-lst-sym take-lst)
ret)
;; interleave two lists
(defun zip (l1 l2 / ret)
(chkargs "zip" '((l1 list ((>= (length l1) (length l2))))
(l2 (list nil))))
(defun-r 'take!)
(defun-q
zip (l1 l2 / ret)
"Returns lists L1 and L2 interleaved"
(declare (vars (l1 list (>= (length l1) (length l2)))
(l2 (list nil)))
(tests (equal (zip '(1 2) '(a b)) '(1 a 2 b))
(equal (zip '(1 2 3) '(a)) '(1 a 2 3))))
(while l1
(add-to-list 'ret (pop! 'l1))
(if l2 (add-to-list 'ret (pop! 'l2))))
ret)
(defun-r 'zip)
;; add an entry for key to alist
;; if alist already contains an entry for key, replace it
;; if so, and the following conditions are met, append to the value of
;; the entry rather than replacing:
;; 1. append-p must be T
;; 2. original entry value and new value types must match
;; 3. types must be STR or LIST
(defun add-to-alist (alist-sym key value append-p / alist old oldval oldtype newtype)
(chkargs "add-to-alist" '((alist-sym sym (sym-lst-p))
(key nil)
(value nil)
(append-p nil)))
(defun-q
add-to-alist (alist-sym key value append-p / alist old oldval oldtype newtype)
"Adds an entry for KEY to ALIST pointed to by ALIST-SYM
If ALIST already contains an entry for KEY, replace it. If so, and the following
conditions are met, append to the value of the entry rather than replacing:
1. APPEND-P must be non-nil
2. Original entry value and new value types must match
3. Types must be STR or LIST"
(setq alist (vl-symbol-value alist-sym)
old (assoc key alist)
oldval (if old (cdr old) nil)
@ -63,211 +119,290 @@
value))
old
alist))
(add-to-list alist-sym (cons key value))))
;; add elt to list referred to by list-sym
(defun add-to-list (list-sym elt)
(chkargs "add-to-list" '((list-sym sym (sym-lst-p))
(elt nil)))
(add-to-list alist-sym (cons key value))))
(declare-late 'add-to-alist
'((vars (alist-sym sym (sym-lst-p alist-sym)))
(tests (equal ((lambda(/ lst) (add-to-alist 'lst 'a 1 nil)))
'((a . 1)))
(equal ((lambda(lst) (add-to-alist 'lst 'a 1 nil))
'((a . 2)))
'((a . 1)))
(equal ((lambda(lst) (add-to-alist 'lst 'a ", world" t))
'((a . "hello")))
'((a . "hello, world"))))))
(defun-r 'add-to-alist)
(defun-q
add-to-list (list-sym elt)
"Adds ELT to list referred to by LIST-SYM"
(set list-sym (append (vl-symbol-value list-sym) (list elt))))
;; add elt to list referred to by lst at pos
(defun add-to-list-at-pos (lst elt pos / beg end)
(chkargs "add-to-list-at-pos"
'((lst sym (sym-lst-p)) (elt nil) (pos int))
) ;_ chkargs
(split-list (eval lst) '(< (vl-position x lst) pos) 'beg 'end)
(append beg (list elt) end)
) ;_ defun add-to-list-at-n
;; remove nth item from list
(defun remove-nth (n lst / i)
(chkargs "remove-nth" '((n int)
(lst list)))
(declare-late 'add-to-list
'((vars (list-sym sym (sym-lst-p list-sym)))
(tests (equal ((lambda(/ lst) (add-to-list 'lst 1))) '(1))
(equal ((lambda(lst) (add-to-list 'lst 2)) '(1)) '(1 2)))))
(defun-r 'add-to-list)
(defun-q
add-to-list-at-pos (lst-sym elt pos / beg end)
"Adds ELT to list referred to by LST-SYM at POS"
(declare (vars (lst-sym sym (sym-lst-p lst-sym))
(pos int))
(tests (equal ((lambda(lst) (add-to-list-at-pos 'lst nil 1)) '(1 2)) '(1 nil 2))))
(split-list (eval lst-sym) '(< (vl-position x (vl-symbol-value lst-sym)) pos) 'beg 'end)
(append beg (list elt) end))
(defun-r 'add-to-list-at-pos)
(defun-q
remove-nth (n lst / i)
"Returns LST with Nth item removed"
(setq i -1)
(vl-remove-if '(lambda (x) (= (inc! 'i) n)) lst))
;; like subst but refer to the list with a symbol and update it
;; in place
(defun subst! (new old lst-sym)
(chkargs "subst!" '((new nil)
(old nil)
(lst-sym sym (sym-lst-p))))
(declare-late 'remove-nth
'((vars (n int)
(lst nil (listp lst)))
(tests (equal (remove-nth 1 '(1 2 3)) '(1 3)))))
(defun-r 'remove-nth)
(defun-q
subst! (new old lst-sym)
"Updates list at LST-SYM in place using subst"
(set lst-sym (subst new old (vl-symbol-value lst-sym))))
;; special version of subst for replacing keys in data list
(defun subst-key (key new-val lst)
(chkargs "subst-key" '((key str nil)
(new-val nil)
(lst list (data-list-p))))
(subst (list key new-val) (assoc key lst) lst))
;; like subst-key but refer to the list with a symbol and
;; update it in place
(defun subst-key! (key new-val lst-sym)
(chkargs "subst-key!" '((key str nil)
(new-val nil)
(lst-sym sym (sym-lst-p))))
(set lst-sym (subst-key key new-val (vl-symbol-value lst-sym))))
;; like vl-remove-if but refer to the list with a symbol
;; This function also automatically includes '(lambda (x) in f
(defun remove (lst f)
(chkargs "remove" '((lst list)
(f list)))
(declare-late 'subst!
'((vars (lst-sym sym (sym-lst-p lst-sym)))
(tests (equal ((lambda(lst) (subst! 'a 1 'lst) lst) '(1)) '(a))
(equal ((lambda(lst) (subst! 'a 1 'lst) lst) '(2)) '(2)))))
(defun-r 'subst!)
(defun-q
remove (lst f)
"Returns LST with only elements for which F returns nil
F should be a quoted list like '(= X 1), where X is the element in question. F will be
processed by %macro-expand."
(declare (vars (lst nil (listp lst))
(f nil (listp f)))
(tests (equal (remove '(1 2 3) '(> x 1)) '(1))
(equal (remove '(1 2 3) '(< x 1)) '(1 2 3))))
(vl-remove-if (append '(lambda (x))
(list (macro-expand f)))
lst))
;; like the above, but in-place
(defun remove! (lst-sym f)
(chkargs "remove!" '((lst-sym sym (sym-lst-p))
(f list)))
(defun-r 'remove)
(defun-q
remove! (lst-sym f)
"Updates list at LST-STM in place using %remove"
(declare (vars (lst-sym sym (sym-lst-p lst-sym))
(f nil (listp f)))
(tests (equal ((lambda(lst) (remove! 'lst '(> x 1)) lst) '(1 2 3)) '(1))
(equal ((lambda(lst) (remove! 'lst '(< x 1)) lst) '(1 2 3)) '(1 2 3))))
(set lst-sym (remove (vl-symbol-value lst-sym) f)))
;; like remove, but using vl-remove-if-not
(defun filter (lst f)
(chkargs "filter" '((lst list)
(f list)))
(defun-r 'remove!)
(defun-q
filter (lst f)
"Returns LST without elements for which F returns nil
F should be a quoted list like '(= X 1), where X is the element in question. F will be
processed by %macro-expand."
(declare (vars (lst nil (listp lst))
(f nil (listp f)))
(tests (equal (filter '(1 2 3) '(< x 2)) '(1))
(equal (filter '(1 2 3) '(> x 0)) '(1 2 3))))
(vl-remove-if-not (append '(lambda (x))
(list f))
lst))
;; same as above, but in-place
(defun filter! (lst-sym f)
(chkargs "filter!" '((lst-sym sym (sym-lst-p))
(f list)))
(defun-r 'filter)
(defun-q
filter! (lst-sym f)
"Updates list at LST-STM in place using %filter"
(declare (vars (lst-sym sym (sym-lst-p lst-sym))
(f nil (listp f))))
(set lst-sym (vl-remove-if-not (append '(lambda (x))
(list f))
(vl-symbol-value lst-sym))))
(defun-r 'filter!)
;; put items for which f returns true in true-lst and the rest in false-lst
(defun split-list (lst f true-lst-sym false-lst-sym / )
(chkargs "split-list" '((lst list)
(true-lst-sym sym (sym-lst-p))
(true-lst-sym sym (sym-lst-p))))
(defun-q
split-list (lst f true-lst-sym false-lst-sym / )
"Puts items for which F returns nil in FALSE-LST and the rest in TRUE-LST
See %remove and %filter for how to format F."
(declare (vars (lst nil (listp lst))
(true-lst-sym sym (sym-lst-p true-lst-sym))
(false-lst-sym sym (sym-lst-p false-lst-sym))))
(set true-lst-sym (filter lst f))
(set false-lst-sym (remove lst f)))
;; split a list on index n
(defun split-list-n (lst n first-lst-sym second-lst-sym / tmp)
(defun-r 'split-list)
(defun-q
split-list-n (lst n first-lst-sym second-lst-sym / tmp)
"Splits LST on index N, putting first half in FIRST-LST and the rest in SECOND-LST"
(declare (vars (lst nil (listp lst))
(n int)
(first-lst-sym sym (sym-lst-p first-lst-sym))
(second-lst-sym sym (sym-lst-p second-lst-sym))))
(repeat n
(add-to-list 'tmp (car lst))
(setq lst (cdr lst)))
(set first-lst-sym tmp)
(set second-lst-sym lst))
;; use vl-sort to sort a list in place
(defun sort! (lst-sym f)
(chkargs "sort!" '((lst-sym sym (sym-lst-p))
(f nil)))
(defun-r 'split-list-n)
(defun-q
sort! (lst-sym f)
"Sorts LST in place using vl-sort"
(declare (vars (lst-sym sym (sym-lst-p lst-sym))
(f nil (function-or-lambda-p f)))
(tests (equal ((lambda(lst) (sort! 'lst '<) lst) '(2 1 3)) '(1 2 3))))
(set lst-sym (vl-sort (vl-symbol-value lst-sym) f)))
(defun-r 'sort!)
;; normal nth errors if the list is nil
;; this version returns nil in that case
(defun safe-nth (n list / )
(if (defined list)
(nth n list)
nil))
(defun-q
safe-nth (n lst / )
"Wrapper for nth that returns nil when LST is nil
;; return a list of unique items in the given list
(defun uniquify (lst / ret)
(chkargs "uniquify" '((lst nil (listp))))
Built-in nth errors in that case."
(declare (vars (n int)
(lst nil (listp lst)))
(tests (= (safe-nth 1 '(1 2)) 2)
(= (safe-nth 1 nil) nil)))
(if lst
(nth n lst)))
(defun-r 'safe-nth)
(defun-q
uniquify (lst / ret)
"Returns LST without duplicate items"
(declare (vars (lst nil (listp lst)))
(tests '(equal (uniquify '(1 2 3 1 2)) '(1 2 3))))
(foreach elt lst
(if (not (member elt ret))
(add-to-list 'ret elt)))
ret)
;; like mapcar, but update the lst-sym in place
(defun mapcar! (f lst-sym)
(defun-r 'uniquify)
(defun-q
mapcar! (f lst-sym)
"Updates list at LST-SYM in place using mapcar"
(declare (vars (f nil (function-or-lambda-p f))
(lst-sym sym (sym-lst-p lst-sym)))
(tests (equal ((lambda(lst) (mapcar! '1+ 'lst) lst) '(1 2 3)) '(2 3 4))))
(set lst-sym (mapcar f (vl-symbol-value lst-sym))))
(defun map-append (f lst)
(defun-r 'mapcar!)
(defun-q
map-append (f lst)
"Alias for (apply 'append (mapcar f lst))"
(declare (vars (f nil (function-or-lambda-p f))
(lst nil (listp lst)))
(tests (equal (map-append 'reverse '((1 2) (3 4))) '(2 1 4 3))))
(apply 'append (mapcar f lst)))
;; test a list of values for type and return nil if any are nil
(defun test-for (test-type lst)
(defun-r 'map-append)
(defun-q
test-for (test-type lst)
"Returns T if all elements of LST are of type TEST-TYPE"
(declare (vars (test-type sym)
(lst nil (listp lst)))
(tests (test-for 'int '(1 2 3))
(test-for 'sym '(a b c))
(not (test-for 'int '(1 2 3.0)))))
(apply 'and (mapcar '(lambda (x) (= test-type (type x))) lst)))
;; combines setq and append functions
(defun append! (lst-sym app-lst / )
(chkargs "append!"
'((lst-sym sym (sym-lst-p (not (= lst-sym 'app-lst)))) (app-lst list))
) ;_ chkargs
(defun-r 'test-for)
(defun-q
append! (lst-sym app-lst / )
"Updates LST-SYM in place using append"
(declare (vars (lst-sym sym (sym-lst-p lst-sym))
(app-lst nil (listp app-lst)))
(tests (equal ((lambda(lst) (append! 'lst '(3)) lst) '(1 2)) '(1 2 3))))
(set lst-sym (append (eval lst-sym) app-lst)))
(defun-r 'append!)
;; builds a list consisting of ele repeated int times
(defun rpt-lst (int ele / lst)
(setq lst '())
(repeat int (append! 'lst (list ele))))
(defun-q
rpt-lst (n ele / lst)
"Returns a list consisting of ELE repeated N times
;; returns a supplied list without the last element
(defun remove-last (lst) (reverse (cdr (reverse lst))))
Duplicate (args reversed) of %lst*."
(declare (vars (n int))
(tests (equal (rpt-lst 3 'q) '(q q q))))
(repeat n (append! 'lst (list ele))))
(defun-r 'rpt-lst)
;; returns true if any keys in a data list are repeated
(defun has-duplicates-p (data / last-key ret)
(sort! 'data '(lambda(a b) (< (car a) (car b))))
(foreach elt data
(if (= (car elt) last-key) (setq ret T))
(setq last-key (car elt)))
ret)
(defun-q
lst* (lstelt n / ret)
"Returns a list consisting of ELE repeated N times
;; nth that allows negative numbers to index from the end of the list
;; or wrap around if n is greater than the list length
(defun circular-nth (n lst / len)
Duplicate (args reversed) of %rpt-lst."
(repeat n (add-to-list 'ret lstelt)))
(defun-r 'lst*)
(declare-late 'lst*
'((vars (n int))
(tests (equal (lst* 'q 3) '(q q q)))))
(defun-q
remove-last (lst)
"Returns LST without its last element"
(declare (vars (lst nil (listp lst)))
(tests (equal (remove-last '(1 2 3)) '(1 2))))
(reverse (cdr (reverse lst))))
(defun-r 'remove-last)
(defun-q
circular-nth (n lst / len)
"Wrapper for nth that treats LST as periodic and infinite
If a negative is supplied for N, index from the end of LST. If N is greater than the
length of LST, wrap around to the beginning again as many times as necessary."
(declare (vars (n int)
(lst nil (listp lst)))
(tests (= (circular-nth 1 '(1 2 3)) 2)
(= (circular-nth -1 '(1 2 3)) 3)
(= (circular-nth 3 '(1 2 3)) 1)
(= (circular-nth 6 '(1 2 3)) 1)))
(setq len (length lst))
(while (< n 0) (setq n (+ n len)))
(while (>= n len) (setq n (- n len)))
(nth n lst))
;; circular left shift a list n times
(defun circular-shift (lst n / tmplst)
(defun-r 'circular-nth)
(defun-q
circular-shift (lst n / tmplst)
"Returns LST left-shifted N times"
(declare (vars (lst nil (listp lst))
(n int))
(tests (equal (circular-shift '(1 2 3) 1) '(2 3 1))
(equal (circular-shift '(1 2 3) 2) '(3 1 2))))
(while (> n 0)
(add-to-list 'tmplst (car lst))
(setq lst (cdr lst)
n (1- n)))
(append lst tmplst))
(defun-r 'circular-shift)
(defun-q
find-first (pred lst / i)
"Returns the first element in LST for which PRED returns non-nil
;; returns the first element in LST for which PRED returns non-nil
;; PRED should be a function of one argument.
(defun find-first (pred lst / i)
PRED should be a function of one argument."
(declare (vars (pred nil (function-or-lambda-p pred))
(lst nil (listp lst)))
(tests (= (find-first '(lambda(a) (> a 1)) '(1 2 3)) 2)))
(setq i -1)
(while (not (apply pred (list (nth (inc! 'i) lst)))))
(nth i lst))
(defun-r 'find-first)
(defun-q
count-items (ci-lst / keys ret)
"Returns a list of counts of items in CI-LST
;; recenter a points list on the vth vertex
(defun rezero-pts (pts v / delta dx dy)
(setq pts (if (= (length (car pts)) 2) (add-bulges pts) pts)
delta (nth v pts)
dx (car delta)
dy (cadr delta))
(circular-shift
(mapcar '(lambda(pt) (list
(- (car pt) dx)
(- (cadr pt) dy)
(caddr pt)))
pts)
v))
;; displace a single point by another point
(defun displace-pt (pt d)
(mapcar '+ pt d))
;; displace all points in pts by d (a single point vector)
(defun displace-pts (pts d)
(mapcar '(lambda(x) (mapcar '+ x d)) pts))
;; rotate a single point about the origin by ang (radians)
(defun rotate-pt (pt ang / x y)
(setq x (car pt)
y (cadr pt))
(list (- (* x (cos ang)) (* y (sin ang)))
(+ (* y (cos ang)) (* x (sin ang)))
(caddr pt)))
;; rotate all points in a list about the origin by ang (radians)
(defun rotate-pts (pts ang)
(mapcar '(lambda(x) (rotate-pt x ang)) pts))
;; count items
(defun count-items (ci-lst / keys ret)
Each itme in the return list is a 2-item list whose car is the element and whose cadr is
the count of that element in CI-LST."
(declare (vars (ci-lst nil (listp ci-lst)))
(tests (equal (count-items '(1)) '((1 1)))
(equal (count-items '(1 1 2 2 2)) '((1 2) (2 3)))))
(foreach x ci-lst
(if (member x keys)
(subst! (list x (1+ (value x ret))) (assoc x ret) 'ret)
@ -275,80 +410,99 @@
(add-to-list 'keys x)
(add-to-list 'ret (list x 1)))))
ret)
;; multiply an element into a list
(defun lst* (lstelt n / ret)
(repeat n (add-to-list 'ret lstelt)))
;; return a list of numbers
;; to is exclusive
(defun range (from to by / ret)
(defun-r 'count-items)
(defun-q
range (from to by / ret)
"Returns a list of numbers that range from FROM to TO by step BY
TO is exclusive"
(declare (vars (from nil (numberp from))
(to nil (numberp to))
(by nil (numberp by)))
(tests (equal (range 1 4 1) '(1 2 3))
(equal (range 0.75 1.25 0.125) '(0.75 0.875 1.0 1.125))))
(setq from (- from by))
(while (< (setq from (+ from by)) to)
(add-to-list 'ret from)))
;; returns a 2-item list from a passed list
;; (2-item-list '(1 2 3 4 a b c d)) returns '((1 2) (3 4) (A B) (C D))
(defun 2-item-list (lst / )
(add-to-list 'ret from)))
(defun-r 'range)
(defun-q
cons-ind-lst (len i / ret)
"Returns a range of integers starting at I (optional) of length LEN, by 1"
(declare (vars (len int)
(i (int nil)))
(tests (equal (cons-ind-lst 3 nil) '(0 1 2))
(equal (cons-ind-lst 3 5) '(5 6 7))))
(setq i (?n i))
(repeat len (add-to-list 'ret i) (setq i (1+ i)))
ret)
(defun-r 'cons-ind-lst)
(defun-q
2-item-list (lst / )
"Returns the elements of LST grouped by twos"
(declare (vars (lst nil (listp lst)))
(tests (equal (2-item-list '(1 2 a b)) '((1 2) (a b)))
(equal (2-item-list '(1 2 a)) '((1 2) (a nil)))))
(if lst
(cons
(list (car lst) (cadr lst))
(2-item-list (cddr lst)))))
;; returns a 3-item list from a passed list
;; (3-item-list '(1 2 3 4 5 6 a b c)) returns '((1 2 3) (4 5 6) (A B C))
(defun 3-item-list (lst / )
(cons (list (car lst) (cadr lst))
(2-item-list (cddr lst)))))
(defun-r '2-item-list)
(defun-q
3-item-list (lst / )
"Returns the elements of LST grouped by threes"
(declare (vars (lst nil (listp lst)))
(tests (equal (3-item-list '(1 2 3 a b c)) '((1 2 3) (a b c)))
(equal (3-item-list '(1 2 3 a b)) '((1 2 3) (a b nil)))))
(if lst
(cons
(list (car lst) (cadr lst) (caddr lst))
(3-item-list (cdddr lst)))))
(defun first-two (lst)
(cons (list (car lst) (cadr lst) (caddr lst))
(3-item-list (cdddr lst)))))
(defun-r '3-item-list)
(defun-q
first-two (lst)
"Returns the first two elements of LST"
(declare (vars (lst nil (listp lst)))
(tests (equal (first-two '(1 2 3)) '(1 2))))
(list (car lst)
(cadr lst)))
;;; constructs and returns an index list with
;;; length of len (optionally begin at index i)
;;; (cons-ind-lst 3 nil) returns (0 1 2)
;;; (cons-ind-lst 3 5) returns (5 6 7)
(defun cons-ind-lst (len i / ret)
(setq i (?n i))
(repeat len (add-to-list 'ret i) (setq i (1+ i)))
ret
) ;_ defun cons-ind-lst
;; remove nils from a list
(defun strip-nil (lst)
(defun-r 'first-two)
(defun-q
strip-nil (lst)
"Returns LST with nil elements removed"
(declare (vars (lst nil (listp lst)))
(tests (equal (strip-nil '(1 nil 2)) '(1 2))
(equal (strip-nil '(1 2 3)) '(1 2 3))))
(remove lst '(null x)))
;; take a chunk of a list
(defun slice (lst start len)
(defun-r 'strip-nil)
(defun-q
slice (lst start len)
"Return a slice from LST of length LEN starting at START"
(declare (vars (lst nil (listp lst))
(start int)
(len int))
(tests (equal (slice '(1 2 3 4) 0 4) '(1 2 3 4))
(equal (slice '(1 2 3 4) 1 2) '(2 3))))
(take! start 'lst)
(take len lst))
;;; passed list is a list of line segments represented by vertices
;;; returns a list of joined segments represented by vertices
;;; (join-vertices '(((0 0) (1 0)) ((1 0) (1 1)) ((1 2) (0 2))))
;;; returns '(((0 0) (1 0) (1 1)) ((1 2) (0 2)))
(defun join-vertices (lst / tmp ret)
(while lst
(setq tmp (pop! 'lst))
(if (equal (car tmp) (last (last ret)))
(subst! (append (last ret) (list (cadr tmp))) (last ret) 'ret)
(append! 'ret (list tmp))
) ;_ if
) ;_ while
(if (and (equal (caar ret) (last (last ret))) (> (length ret) 1))
(progn
(setq tmp (cdar ret) ret (cdr ret))
(subst! (append (last ret) tmp) (last ret) 'ret)
) ;_ progn
) ;_ if
ret
) ;_ defun join-vertices
;; determine if any members of list1 appear in list2
(defun member* (list1 list2)
(if (listp list1)
(vl-member-if '(lambda(x) (member x list2)) list1)
(member list1 list2)))
(defun-r 'slice)
(defun-q
member* (target list2)
"Returns T if any members of TARGET appear in LST
TARGET can be a list or atom."
(declare (vars (list2 nil (listp list2)))
(tests (member 'a '(a b))
(not (member 'c '(a b)))
(member* '(a 1) '(a b))
(member* '(a 1) '(1 2))
(not (member* '(a b) '(1 2)))))
(if (listp target)
(vl-member-if '(lambda(x) (member x list2)) target)
(member target list2)))
(defun-r 'member*)

@ -1,48 +1,46 @@
(defun object-load (name)
(load (strcat "util/object/" name ".lsp")))
(set-file-docstring
"VLA Object-handling functions")
(mapcar 'object-load '(
"3D" ; solids, regions, extrusion and subtraction
"block" ; block definitions and references
"line" ; lines, polylines
"table" ; tables
"text" ; text, mtext, mleaders
))
(defun-q
add-calcd-objs (container extrude-p defs / otype ret)
"Processes calculated objects for 2D and 3D blocks
(defun delete&purge (ename / blkRef-p blkTblObj blkInsObj blkDefObj)
(setq
blkTblObj (vla-get-blocks acaddoc)
blkInsObj (ename>vlobj ename)
blkDefObj (vla-item blkTblObj (vla-get-name blkInsObj))
) ;_ setq
(vla-delete blkInsObj)
(if
(member (vla-get-name blkDefObj)
(mapcar 'vla-get-name
(vl-remove-if-not
'blkRef-p
(collection->list (vla-item blkTblObj 0))
) ;_ vl-remove-if-not
) ;_ mapcar
) ;_ member
(error "block cannot be purged, multiple references found")
(vla-delete blkDefObj)
) ;_ if
) ;_ defun get-ins-delete-and-purge
;; process all calcs objects in lst
;; if extrude-p is non-nil, make it 3D
(defun add-calcd-objs (container extrude-p lst / otype ret)
(foreach obj lst
(setq otype (value "type" (car obj)))
(cond ((= otype "poly") (add-to-list 'ret (add-calcd-poly container extrude-p obj)))
((= otype "pipe") (append! 'ret (add-calcd-pipe container extrude-p obj)))))
CONTAINER should be the target block definition or model space. If EXTRUDE-P is non-nil,
creates 3D solids. Otherwise, creates a 2D wireframe. Additionally, when EXTRUDE-P is
non-nil, the object will be translated into the perspective used in 3D drawings.
DEFS is a list of calculated objects. Each is a list whose car is a data list of object
properties and whose cdr is the particular object definition. The value of the type key in
the properties determines which handling function is called. The naming scheme for
handling functions is 'add-calcd-' followed by the type value which triggers it.
Available handling functions:
- %add-calcd-poly
- %add-calcd-pipe
Common properties:
- ins: insert point (all pts in definition displaced relative to ins)
- layer: layer to put obj on
- extrude: extrusion distance"
(declare (vars (container VLA-Object)
(defs nil (listp defs))))
(foreach def defs
(setq otype (value "type" (car def)))
(cond ((= otype "poly") (add-to-list 'ret (add-calcd-poly container extrude-p def)))
((= otype "pipe") (append! 'ret (add-calcd-pipe container extrude-p def)))))
ret)
(defun-r 'add-calcd-objs)
(defun-q
add-calcd-poly (container extrude-p def / data obj obj2 ins)
"Adds a calculated polyline to CONTAINER
;; add a calculated polyline to container
(defun add-calcd-poly (container extrude-p def / data obj obj2 ins)
CONTAINER should be the target block definition or model space. DEF should be a list whose
car is a data list of properties and whose cdr is a list of points representing a closed
polyline. If EXTRUDE-P is non-nil, the closed polyline is extruded by the value of the
\"extrude\" property."
(setq data (car def)
ins (if (assoc "ins" data) (value "ins" data) '(0 0 0))
ins (?? (xd-value "ins" data) '(0 0 0))
obj (add-polyline container
(if extrude-p ins (first-two ins))
(cdr def)
@ -50,42 +48,40 @@
(if extrude-p
(progn
(setq obj (plineObj->solidObj obj (value "extrude" data) T))
(vla-Rotate3D obj (3dpt 0 0) (3dpt 1 0) (dtr 90))
(if (assoc "layer" data) (vla-put-Layer obj (value "layer" data)))
obj
) ;_ progn
(progn
(if (assoc "layer" data) (vla-put-Layer obj (value "layer" data)))
obj
) ;_ progn
) ;_ if
) ;_ defun
;; add a calculated pipe to container
(defun add-calcd-pipe (container extrude-p def / data pts obj obj2)
(vla-Rotate3D obj (3dpt 0 0) (3dpt 1 0) (dtr 90))))
((lambda(layer)
(if layer (vla-put-Layer obj layer)))
(xd-value "layer" data))
obj)
(defun-r 'add-calcd-poly)
(defun-q
add-calcd-pipe (container extrude-p def / data pts obj obj2)
"Adds a calculated pipe to CONTAINER
Pipe objects represent bent pipe handrails. They are represented as open polylines. This
function fillets the points list and either offsets or extrudes the pipe.
Addition properties available to pipes:
- radius: fillet radius
- bulge: used as 4th argument to %offset&close"
(setq data (car def)
pts (cdr def))
(if (assoc "ins" data) (setq pts (displace-pts pts (value "ins" data))))
((lambda(ins)
(if ins (setq pts (displace-pts pts ins))))
(xd-value "ins" data))
(if extrude-p
( (lambda (pts)
(setq obj (vla-Add3DPoly container (vlist->safearray pts)))
(vla-put-Layer obj "NO PLOT")
(setq obj2
(draw-pipe-3d
(if (> (length pts) 2)
(draw-fillet-3d container (safe-calc-fillet-3d pts 2.83))
(list (vla-Add3DPoly container (vlist->safearray pts)))
) ;_ if
(value "radius" data)
) ;_ draw-pipe-3d
) ;_ setq
(if (assoc "layer" data)
(foreach obj obj2 (vla-put-Layer obj (value "layer" data)))
) ;_ if
(list obj obj2)
) ;_ lambda
(mapcar '(lambda (p) (trans p '(0 -1 0) 0)) pts)
) ;_ lambda_func
((lambda (pts)
(setq obj (vla-Add3DPoly container (vlist->safearray pts)))
(vla-put-Layer obj "NO PLOT")
(setq obj2 (draw-pipe-3d (if (> (length pts) 2)
(draw-fillet-3d container (safe-calc-fillet-3d pts 2.83))
(list (vla-Add3DPoly container (vlist->safearray pts))))
(value "radius" data)))
(if (assoc "layer" data)
(foreach obj obj2 (vla-put-Layer obj (value "layer" data))))
(list obj obj2))
(mapcar '(lambda (p) (trans p '(0 -1 0) 0)) pts))
(progn
(setq pts (fillet-all-pts (uniquify (mapcar 'first-two pts)) 2.83)
obj (add-polyline container '(0 0 0) pts nil)
@ -93,8 +89,53 @@
(if (assoc "layer" data)
(vla-put-Layer obj2 (value "layer" data)))
(vla-Delete obj)
(list obj2)
) ;_ progn
) ;_ if
) ;_ defun
(list obj2))))
(defun-r 'add-calcd-pipe)
(defun-q
vla-mirror-y (obj)
"Mirrors VLA-object OBJ over Y-axis using a transformation matrix"
(declare (vars (obj vla-object)))
(vla-TransformBy obj
(vlax-tmatrix '((-1 0 0 0)
(0 1 0 0)
(0 0 1 0)
(0 0 0 1)))))
(defun-r 'vla-mirror-y)
(defun-q
vla-mirror-x (obj)
"Mirrors VLA-object OBJ over x-axis using a transformation matrix"
(declare (vars (obj vla-object)))
(vla-TransformBy obj
(vlax-tmatrix '((1 0 0 0)
(0 -1 0 0)
(0 0 1 0)
(0 0 0 1)))))
(defun-r 'vla-mirror-x)
(defun-q
mirror&delete (obj pt axis / pt2 ret)
"Mirrors OBJ over AXIS at PT, deletes the original, and returns the copy
OBJ should be a VLA-object. Axis should be one of the symbols 'x or 'y. PT should be a
point."
(declare (vars (obj vla-object)
(pt list (point-p pt))
(axis sym (member axis '(x y)))))
(setq pt2 (3dpt (displace-pt pt (if (= axis 'x) '(1 0) '(0 1))))
ret (vla-Mirror obj (3dpt pt) pt2))
(vla-delete obj)
ret)
(defun-r 'mirror&delete)
(defun object-load (name)
(psc-load (strcat "util/object/" name ".lsp")))
(mapcar 'object-load '(
"3D" ; solids, regions, extrusion and subtraction
"block" ; block definitions and references
"line" ; lines, polylines
"table" ; tables
"text" ; text, mtext, mleaders
))

@ -1,137 +1,221 @@
;; creates extruded 3D solid from a closed pline input ext-param defines
;; extrusion type and must be a number or a vl-obj representing a path
(defun plineObj->solidObj (plineObj ext-param delete / regionObj solidObj)
(setq regionObj (plineObj->regionObj plineObj delete))
(setq solidObj
(if (numberp ext-param)
(vla-addextrudedsolid
(block-containing regionObj)
regionObj
ext-param
0
) ;_ vla-add-extrudedsolid
(vla-addextrudedsolidalongpath
(block-containing regionObj)
regionObj
ext-param
) ;_ vla-AddExtrudedSolidAlongPath
) ;_ if
) ;_ setq
(vla-delete regionObj)
(if (and delete (not (numberp ext-param)))
(vla-delete ext-param)
) ;_ if
(if (and delete (not (vlax-erased-p plineObj)))
(vla-delete plineObj)
) ;_ if
solidObj
) ;_ defun plineObj->solidObj
(set-file-docstring "Functions related to creation of 3D objects")
;; creates a region from a closed pline input
(defun plineObj->regionObj (plineObj delete / regionObj)
(setq regionObj
(vlax-safearray-get-element
(vlax-variant-value
(defun-q closed-pline-p (plineObj)
"Returns T if passed PLINEOBJ is a closed polyline"
(declare (vars (plineObj vla-object)))
(or
(= "AcDbCircle" (vla-get-objectname plineObj))
(and
(= "AcDbPolyline" (vla-get-objectname plineObj))
(= :vlax-true (vla-get-closed plineObj))
) ;_ and
) ;_ or
) ;_ defun-q closed-pline-p
(defun-r 'closed-pline-p)
(defun-q
path-p (obj)
"Returns T if passed OBJ is a valid path"
(declare (vars (obj vla-object)))
(member
(vla-get-objectname obj)
'("AcDbLine" "AcDbPolyline" "AcDb3dPolyline" "AcDbSpline"
"AcDbArc" "AcDbCircle" "AcDbEllipse")
) ;_ member
) ;_ defun-q path-p
(defun-r 'path-p)
(defun-q plineObj->solidObj (plineObj ext-param delete / regionObj solidObj)
"Creates extruded 3D solid from passed polyline object
PLINEOBJ must be a VLA-OBJ which is closed
EXT-PARAM must be a number for depth or a VLA-OBJECT for path
PLINEOBJ will be deleted if DELETE is non-nil"
(declare
(vars
(plineObj vla-object (closed-pline-p plineObj))
(ext-param nil (or (numberp ext-param) (path-p ext-param)))
) ;_ vars
) ;_ declare
(setq regionObj (plineObj->regionObj plineObj delete))
(setq solidObj
(if (numberp ext-param)
(vla-addextrudedsolid
(block-containing regionObj)
regionObj
ext-param
0
) ;_ vla-add-extrudedsolid
(vla-addextrudedsolidalongpath
(block-containing regionObj)
regionObj
ext-param
) ;_ vla-AddExtrudedSolidAlongPath
) ;_ if
) ;_ setq
(vla-delete regionObj)
(if (and delete (not (numberp ext-param))) (vla-delete ext-param))
(if (and delete (not (vlax-erased-p plineObj))) (vla-delete plineObj))
solidObj
) ;_ defun-q plineObj->solidObj
(defun-r 'plineObj->solidObj)
(defun-q plineObj->regionObj (plineObj delete / regionObj)
"Creates a region from passed polyline object
PLINEOBJ must be a VLA-OBJ which is closed
PLINEOBJ will be deleted if DELETE is non-nil"
(declare (vars (plineObj vla-object (closed-pline-p plineObj))))
(setq regionObj
(vlax-safearray-get-element
(vlax-variant-value
(vla-AddRegion
(car
(vl-remove-if-not
'(lambda (obj)
(= (vla-get-ownerid plineObj) (vla-get-objectid obj))
) ;_ lambda
(collection->list (vla-get-Blocks acadDoc))
) ;_ vl-remove-if-not
(car
(vl-remove-if-not
'(lambda (obj)
(= (vla-get-ownerid plineObj) (vla-get-objectid obj))
) ;_ lambda
(collection->list (vla-get-Blocks acadDoc))
) ;_ vl-remove-if-not
) ;_ car
(vlax-safearray-fill
(vlax-make-safearray vlax-vbObject '(0 . 0))
(list PlineObj)
(vlax-safearray-fill
(vlax-make-safearray vlax-vbObject '(0 . 0))
(list PlineObj)
) ;_ vlax-safearray-fill
) ;_ vla-AddRegion
) ;_ vlax-variant-value
0
) ;_vlax-safearray-get-element
) ;_ setq
(if (and delete (not (vlax-erased-p plineObj)))
(vla-delete plineObj)
) ;_ if
regionObj
) ;_ defun plineObj->regionObj
) ;_ vla-AddRegion
) ;_ vlax-variant-value
0
) ;_vlax-safearray-get-element
) ;_ setq
(if (and delete (not (vlax-erased-p plineObj))) (vla-delete plineObj))
regionObj
) ;_ defun-q plineObj->regionObj
(defun-r 'plineObj->regionObj)
(defun-q plineObj->faceObj (plineObj dpt delete / vlist faceObj)
"creates 3D face from an open pline input of exactly two vertices"
(declare
(vars
(plineObj
vla-object
(= "AcDbPolyline" (vla-get-objectname plineObj))
( (lambda (lst) (= 2 (length (safearray->list lst))))
(variant-value (vla-get-coordinates plineobj))
) ;_ lambda
) ;_ plineObj-vars
) ;_ vars
) ;_ declare
(setq vlist
(safearray->list (variant-value (vla-get-coordinates plineobj)))
vlist
(append
(mapcar '(lambda (p) (append p '(0.0))) vlist)
(mapcar '(lambda (p) (append p (list dpt))) (reverse vlist))
) ;_ append
) ;_ setq
(setq faceObj
(apply 'vla-add3dface
(append (list (block-containing plineObj)) (mapcar '3dpt vlist))
) ;_ apply
) ;_ setq
(if (and delete (not (vlax-erased-p plineObj))) (vla-delete plineObj))
faceObj
) ;_ defun-q plineObj->faceObj
(defun-r 'plineObj->faceObj)
;; creates 3D face from pline input
(defun plineObj->faceObj (plineObj dpt delete / vlist faceObj)
(setq
vlist
(safearray->list (variant-value (vla-get-coordinates plineobj)))
vlist
(append
(mapcar '(lambda (p) (append p '(0.0))) vlist)
(mapcar '(lambda (p) (append p (list dpt))) (reverse vlist))
) ;_ append
) ;_ setq
(setq faceObj
(apply 'vla-add3dface
(append (list (block-containing plineObj)) (mapcar '3dpt vlist))
) ;_ apply
) ;_ setq
(if (and delete (not (vlax-erased-p plineObj)))
(vla-delete plineObj)
) ;_ if
faceObj
) ;_ plineObj->faceObj
(defun-q put-circle-normal-to-path (obj path / start 3dpos xord yord zord pt)
"Move a passed circle object normal to a passed path object"
(declare
(vars
(obj vla-object (= "AcDbCircle" (vla-get-objectname obj)))
(path vla-object (path-p path))
) ;_ vars
) ;_ declare
(setq start (vlax-curve-getstartpoint path))
(setq 3dpos
(vlax-curve-getFirstDeriv
path
(vlax-curve-getParamAtPoint path start)
) ;_ vlax-curve-getFirstDeriv
) ;_ setq
(setq
xord (if (zerop (car 3dpos)) 0.0 (/ (car 3dpos) 1000))
yord (if (zerop (cadr 3dpos)) 0.0 (/ (cadr 3dpos) 1000))
zord (if (zerop (caddr 3dpos)) 0.0 (/ (caddr 3dpos) 1000))
3dpos (list xord yord zord)
pt (vla-get-center obj)
) ;_ setq
(vla-move obj pt (3dpt start))
(vlax-put-property-r
obj
'normal
(vlax-3D-point 3dpos)
) ;_ vla-put-normal
) ;_ defun-q put-circle-normal-to-path
(defun-r 'put-circle-normal-to-path)
(defun put-normal-to-path (obj pt path / start 3dpos xord yord zord)
(setq start (vlax-curve-getstartpoint path))
(setq 3dpos
(vlax-curve-getFirstDeriv
path
(vlax-curve-getParamAtPoint path start)
) ;_ vlax-curve-getFirstDeriv
) ;_ setq
(setq
xord (if (zerop (car 3dpos)) 0.0 (/ (car 3dpos) 1000))
yord (if (zerop (cadr 3dpos)) 0.0 (/ (cadr 3dpos) 1000))
zord (if (zerop (caddr 3dpos)) 0.0 (/ (caddr 3dpos) 1000))
3dpos (list xord yord zord)
) ;_ setq
(vla-move obj (3dpt pt) (3dpt start))
(vlax-put-property-r
obj
'normal
(vlax-3D-point 3dpos)
) ;_ vla-put-normal
) ;_ defun put-normal-to-path
(defun-q draw-fillet-3d (container def-lst / xyzb-rem-z ret)
"Creates a series of filleted plines from a definition list
(defun draw-fillet-3d (container def-lst / xyzb-rem-z ret)
(defun xyzb-rem-z (xyzb)
(mapcar '(lambda (xyzb) (remove-nth 2 xyzb)) xyzb)
DEF-LST is the return from %safe-calc-fillet-3d"
(declare
(vars
(container vla-object)
(def-lst
list
(vl-every ;vertices
'(lambda (lst) (vl-every 'point-p (mapcar 'remove-last lst)))
(mapcar 'car def-lst)
) ;_ mapcar
(vl-every ;bulge
'(lambda (lst) (vl-every 'numberp (mapcar 'last lst)))
(mapcar 'car def-lst)
) ;_ mapcar
(vl-every 'point-p (mapcar 'last def-lst)) ;norm
) ;_ def-lst-vars
) ;_ vars
) ;_ declare
(defun xyzb-rem-z (xyzb)
(mapcar '(lambda (xyzb) (remove-nth 2 xyzb)) xyzb)
) ;_ defun xyzb-rem-z
(foreach def def-lst
(apply
'(lambda (lst nor / obj)
(setq obj
(add-polyline container '(0 0 0) (xyzb-rem-z lst) nil)
) ;_ setq
(vla-put-normal obj (3dpt nor))
(vla-move
obj
(3dpt (vlax-curve-getstartpoint obj))
(3dpt (trans (remove-last (car lst)) nor 0))
) ;_ vla-move
(add-to-list 'ret obj)
) ;_ lambda
def
) ;_ apply
) ;_ foreach
) ;_ defun draw-fillet-3d
(foreach def def-lst
(apply
'(lambda (lst nor / obj)
(setq obj
(add-polyline container '(0 0 0) (xyzb-rem-z lst) nil)
) ;_ setq
(vla-put-normal obj (3dpt nor))
(vla-move
obj
(3dpt (vlax-curve-getstartpoint obj))
(3dpt (trans (remove-last (car lst)) nor 0))
) ;_ vla-move
(add-to-list 'ret obj)
) ;_ lambda
def
) ;_ apply
) ;_ foreach
) ;_ defun-q draw-fillet-3d
(defun-r 'draw-fillet-3d)
(defun draw-pipe-3d (path-lst rad / cen cir ret)
(foreach path-obj path-lst
(setq
cen (vlax-curve-getStartPoint path-obj)
cir (vla-addCircle (block-containing path-obj) (3dpt cen) rad)
) ;_ setq
(put-normal-to-path cir cen path-obj)
(add-to-list 'ret (plineObj->solidObj cir path-obj T))
) ;_ foreach
ret
(defun-q draw-pipe-3d (path-lst rad / cen cir ret)
"Create a series of extruded circles along list of path objects"
(declare
(vars
(path-lst
nil
(vl-every '(lambda (obj) (= (type obj) 'vla-object)) path-lst)
) ;_ path-lst-vars
) ;_ vars
) ;_ declare
(foreach path-obj path-lst
(setq
cen (vlax-curve-getStartPoint path-obj)
cir (vla-addCircle (block-containing path-obj) (3dpt cen) rad)
) ;_ setq
(put-circle-normal-to-path cir path-obj)
(add-to-list 'ret (plineObj->solidObj cir path-obj T))
) ;_ foreach
ret
) ;_ defun draw-pipe-3d
(defun-r 'draw-pipe-3d)

@ -1,79 +1,102 @@
;; return a block definition from an ename
(defun blockdef (ename)
(set-file-docstring
"Block-handling functions")
(defun-q
blockdef (ename)
"Returns the block definition for insert with ENAME"
(declare (vars (ename ename)))
(vla-Item (vla-get-Blocks acadDoc)
(vla-get-EffectiveName (ename>vlobj ename))))
(defun-r 'blockdef)
;; return a block reference's definition
(defun blockref2def (blockref)
(defun-q
blockref2def (blockref)
"Returns the block definition for insert BLOCKREF"
(declare (vars (blockref vla-object)))
(vla-Item (vla-get-Blocks acadDoc)
(vla-get-EffectiveName blockref)))
(defun-r 'blockref2def)
(defun-q
blkRef-p (obj)
"Returns T if OBJ is a block reference"
(declare (vars (obj vla-object)))
(= "AcDbBlockReference" (vla-get-objectname obj)))
(defun-r 'blkRef-p)
;; returns true if vlobj is a block reference
(defun blkRef-p (vlobj)
(= "AcDbBlockReference" (vla-get-objectname vlobj)))
(defun-q
define-block (name plines / blocks blockObj)
"Returns a block definition named NAME consisting of closed polylines
;; define a block consisting of closed polylines on layer 0,
;; inserted at the origin
;; return the block object
(defun define-block (name plines / blocks blockObj)
(chkargs "define-block" '((name str)
(plines list ((vl-every 'pts-list-p plines)))))
PLINES is a list of closed polylines inserted at the origin."
(declare (vars (name str)
(plines nil (listp plines) (vl-every 'pts-list-p plines))))
(setq blocks (vla-get-Blocks acadDoc)
blockObj (vla-Add blocks (vlax-3d-point 0 0 0) name))
(foreach pline plines
(vla-put-Layer (add-polyline blockObj '(0 0 0) pline T) "0"))
blockObj)
(defun-r 'define-block)
;; define block only if it doesn't exist already
(defun define-block-maybe (name plines)
(defun-q
define-block-maybe (name plines)
"Calls %define-block only if no block with NAME exists yet"
(declare (vars (name str)
(plines nil (listp plines) (vl-every 'pts-list-p plines))))
(if (not (tblsearch "block" name))
(define-block name plines)))
(defun-r 'define-block-maybe)
(defun-q
insert-block (container ins block)
"Wrapper for vla-InsertBlock
;; convenience wrapper for vla-InsertBlock
;; block is either a string name or a vla block object
;; ins is either a 3d point list or variant
(defun insert-block (container ins block)
BLOCK may be the block name as a string or a block definition obj. INS may be a point list
or vlax point variant."
(declare (vars (container vla-object)
(ins (list variant nil))
(block (str vla-object))))
(if (/= (type block) 'STR)
(setq block (vla-get-Name block)))
(if (= (type ins) 'LIST)
(if (listp ins)
(setq ins (3dpt ins)))
(vla-InsertBlock container ins block 1 1 1 0))
(defun-r 'insert-block)
;; insert a block from the block library
(defun insert-lib-block (container ins name scale)
(defun-q
insert-lib-block (container ins name scale)
"Inserts a block from custom block library
NAME should be the filename of a drawing from the blocks/ folder without the extension."
(declare (vars (container vla-object)
(ins (list safearray nil))
(name str)
(scale nil (numberp scale))))
(vla-AttachExternalReference container
(findfile (strcat "blocks/" name ".dwg"))
name
(3dpt ins)
scale scale 1 0 :vlax-false))
(defun-r 'insert-lib-block)
;; searches the blocks collection for a Block by name
;; returns VL Object name if it is found, otherwise nil
(defun vla-block-defined (blockName)
(chkargs "vla-block-defined" '((blockName str)))
(car
(vl-remove-if-not
'(lambda (obj) (= (strcase blockName) (strcase (vla-get-name obj))))
(collection->list (vla-get-Blocks acaddoc))
) ;_ vl-remove-if-not
) ;_ car
) ;_ defun vla-block-defined
(defun-q
block-containing (sourceObj)
"Searches for the block definition that contains SOURCEOBJ
;; searches the blocks collection for a Block by owner ID of a passed
;; source object returns VL Object name if it is found, otherwise nil
(defun block-containing (sourceObj)
Returns the block name if found, or nil."
(declare (vars (sourceobj vla-object)))
(car
(vl-remove-if-not
'(lambda (obj)
(= (vla-get-ownerid sourceObj) (vla-get-objectid obj))
) ;_ lambda
(collection->list (vla-get-Blocks acadDoc))
) ;_ vl-remove-if-not
) ;_ car
) ;_ defun block-containing
(= (vla-get-ownerid sourceObj) (vla-get-objectid obj)))
(collection->list (vla-get-Blocks acadDoc)))))
(defun-r 'block-containing)
;; moves obj to bottom of draw order
(defun move-to-bottom (obj container / edict sortents sarr)
(defun-q
move-to-bottom (obj container / edict sortents sarr)
"Moves OBJ to bottom of draw order in CONTAINER"
(declare (vars (obj vla-object)
(container vla-object)))
(setq edict (vla-GetExtensionDictionary container)
sortents (vl-catch-all-apply 'vla-GetObject (list edict "ACAD_SORTENTS")))
(if (= (type sortents) 'vl-catch-all-apply-error)
@ -82,10 +105,11 @@
(vlax-safearray-put-element sarr 0 obj)
(vla-MoveToBottom sortents sarr)
obj)
(defun-r 'move-to-bottom)
;; returns a pseudonymous block name
;; designed to replace *U so that "anonymous" blocks can be refedited
(defun p-blk-name (str)
(defun-q
p-blk-name (str)
"Returns a unique pseudonymous block name"
(vl-propagate 'p-blk-name-i)
(setq p-blk-name-i (1+ (?n p-blk-name-i)))
(if str
@ -95,7 +119,10 @@
(rtos (getvar 'cdate) 2 4)
"."
(itoa p-blk-name-i)))
(defun-r 'p-blk-name)
;; return a pseudonymous block
(defun p-blk (str)
(defun-q
p-blk (str)
"Returns a new empty pseudonymous block"
(define-block (p-blk-name str) nil))
(defun-r 'p-blk)

@ -1,8 +1,18 @@
;; add a polyline to vlax objext 'container' (typically modelSpace)
;; ins is insertion point (standard list of 3)
;; vertices is a list of 3-pt lists: (x y bulge)
;; closed-p is T or nil for closing the pline
(defun add-polyline (container ins vertices closed-p / points pline i)
(set-file-docstring
"Polyline handling functions")
(defun-q
add-polyline (container ins vertices closed-p / points pline i)
"Adds a LWPolyline to CONTAINER at INS
INS should be a 2D or 3D point. VERTICES should be a list of points. Z-coordinates in
VERTICES are treated as bulge values (all points are 2D). Points without Z-coordinates
have 0 added to the end.
If CLOSED-P is non-nil, set the Closed property to True."
(declare (vars (container vla-object)
(ins list (point-p ins))
(vertices list (vl-every 'point-p vertices))))
(setq vertices (add-bulges vertices)
points (vlist->safearray (mapcar '(lambda(x) (reverse (cdr (reverse x)))) vertices))
pline (vla-AddLightweightPolyline container points)
@ -16,166 +26,144 @@
(apply 'vlax-3d-point ins))
(vla-Update pline)
pline)
(defun-r 'add-polyline)
(defun-q
add-rectangle (container width height ins)
"Adds a rectangular LWPolyline to CONTAINER using %add-polyline
;; convenience function
;; ins is bottom-left corner
(defun add-rectangle (container width height ins)
INS is at the bottom-left corner of the rectangle. To put rectangles in other quadrants,
supply negative values for WIDTH, HEIGHT, or both."
(declare (vars (container vla-object)
(width nil (numberp width))
(height nil (numberp width))
(ins list (point-p ins))))
(add-polyline container
ins
(rect-pts width height '(0 0 0))
t))
(defun-r 'add-rectangle)
;; convenience function to offset a polyline
;; vla-Offset returns an array for arcane reasons, and we only care
;; about the first element
(defun offset-polyline (plineObj dist / offsetObj)
(setq offsetObj (vlax-safearray-get-element (vlax-variant-value (vla-Offset plineObj dist))
(defun-q
offset-polyline (pline dist / offsetObj)
"Offsets PLINE by DIST and returns the resulting polyline
Essentially a wrapper for VLA-Offset that cracks the acorn and extracts the delicious
offset polyline hidden within a hard outer shell of safearray."
(declare (vars (pline vla-object)
(dist nil (numberp dist))))
(setq offsetObj (vlax-safearray-get-element (vlax-variant-value (vla-Offset pline dist))
0)))
(defun-r 'offset-polyline)
(defun-q
offset&close (pline dist dual bulge / offsetObj offset-pts n-pts offset-bulges)
"Offsets PLINE by DIST and joins both lines into a closed polygon
DIST being negative means to offset in the negative direction. If DUAL is non-nil, offset
in both directions and deletes the original line.
;; offset plineObj by dist, reverse the order of vertices in created
;; offset object and append them to plineObj, then close plineObj
;; if dual is non-nil, offset in both directions and join the two plines
;; if bulge is non-nil, include bulges at either end
;; bulge may be a scalar value indicating the bulge for both ends or a 2-item list
(defun offset&close (plineObj dist dual bulge / offsetObj offset-pts n-pts offset-bulges)
(if dual (setq plineObj (offset-polyline plineObj dist)
If BULGE is non-nil, either or both of the closing lines will be bulged out to a
semicircle. If BULGE is a non-nil scalar, both ends will be bulged. BULGE may also be a
2-item list, with the boolean value of the car indicating whether to bulge the end at the
head of the original line, and the cadr meaning the tail end."
(declare (vars (pline vla-object)
(dist nil (numberp dist))))
(if dual (setq pline (offset-polyline pline dist)
dist (* dist -2)))
(if (numberp bulge) (setq bulge (list bulge bulge)))
(setq offsetObj (offset-polyline plineObj dist)
(setq offsetObj (offset-polyline pline dist)
offset-pts (reverse (safearray->list (vlax-variant-value (vla-get-Coordinates offsetObj))))
n-pts (length (safearray->list (vlax-variant-value (vla-get-Coordinates plineObj))))
n-pts (length (safearray->list (vlax-variant-value (vla-get-Coordinates pline))))
last-vertex (1- n-pts))
(if bulge
(vla-SetBulge plineObj last-vertex (cadr bulge)))
(vla-SetBulge pline last-vertex (cadr bulge)))
(setq v-offset 2)
(foreach pt offset-pts
(vla-AddVertex plineObj (inc! 'last-vertex) (vlist->safearray (list pt)))
(vla-AddVertex pline (inc! 'last-vertex) (vlist->safearray (list pt)))
(if (not (= pt (last offset-pts)))
(progn
(vla-SetBulge plineObj last-vertex (- (vla-GetBulge plineObj (- last-vertex v-offset))))
(vla-SetBulge pline last-vertex (- (vla-GetBulge pline (- last-vertex v-offset))))
(setq v-offset (+ v-offset 2)))))
(vla-put-Closed plineObj :vlax-true)
(vla-put-Closed pline :vlax-true)
(if bulge
(vla-SetBulge plineObj last-vertex (car bulge)))
(vla-SetBulge pline last-vertex (car bulge)))
(vla-Delete offsetObj)
(vla-Update plineObj)
plineObj)
(vla-Update pline)
pline)
(defun-r 'offset&close)
;; call offset&close with dual T, delete the original polyline, return the offset polyline
(defun dualoffset&delete (plineObj dist bulge / offsetObj)
(setq offsetObj (offset&close plineObj dist T bulge))
(vla-Delete plineObj)
(defun-q
dualoffset&delete (pline dist bulge / offsetObj)
"Convenience wrapper for %offset&close
Supplies t for DUAL argument, deletes the original polyline, and returns the resulting
offset polyline."
(declare (vars (pline vla-object)
(dist nil (numberp dist))))
(setq offsetObj (offset&close pline dist T bulge))
(vla-Delete pline)
offsetObj)
(defun-r 'dualoffset&delete)
(defun-q
add-slot (container ins len dia / obj ret)
"Adds a slot polyline to CONTAINER
;; creates a slot given the length and diameter
(defun add-slot (con ins lng dia / obj ret)
(setq obj
(add-polyline
con
ins
(mapcar '(lambda (x) (list (* (- lng dia) x) 0 0)) '(0.5 -0.5))
nil
) ;_ add-polyline
) ;_ setq
(setq ret (offset&close obj (/ dia 2.0) T 1))
INS is in the middle of the centerline and the slot extends horizontally to the left and
right by half of LEN. LEN describes the extents of the hole, including radius, not just
the centerline."
(declare (vars (container vla-object)
(ins list (point-p ins))
(len nil (numberp len))
(dia nil (numberp dia))))
(setq pts (mapcar '(lambda (x) (list (* (- len dia) x) 0 0))
'(0.5 -0.5))
obj (add-polyline container ins pts nil)
ret (offset&close obj (/ dia 2.0) T 1))
(vla-delete obj)
ret
) ;_ defun
ret)
(defun-r 'add-slot)
;; return a lits of points in the form (x y bulge) from a 2D polyline
(defun get-vlist (plineObj / xypts i)
(setq xypts (safearray->list (vlax-variant-value (vla-get-Coordinates plineObj)))
(defun-q
get-vlist (pline / xypts i)
"Returns the list of vertices comprising PLINE in the form (x y bulge)"
(declare (vars (pline vla-object)))
(setq xypts (safearray->list (vlax-variant-value (vla-get-Coordinates pline)))
i -1)
(while (< i (- (length xypts) 1))
(add-to-list 'bulges (vla-getbulge plineObj (inc! 'i))))
(add-to-list 'bulges (vla-getbulge pline (inc! 'i))))
(mapcar '(lambda (a b) (append a (list b))) xypts bulges))
(defun-r 'get-vlist)
;; l1 & l2 represent centerline in format (end-ang end-x end-y)
;; l3 represents offset width and extrusion depth in format (wid dpt)
;; if l3 fails (numberp dpt) wid is assumed to be diameter of circle
;; to be extruded along line path defined by x/y ends in l1 & l2
(defun offset@angle (l1 l2 l3 / off ins pt1 pt2 ang an1 an2 pth obj)
(defun off (a)
(mapcar
'(lambda (f) (* (/ (/ (car l3) 2.0) (sin a)) (f (+ ang a))))
(list cos sin)
) ;_ mapcar
) ;_ defun off
(setq
ins (cdr l1)
pt1 (list 0 0)
pt2 (mapcar '- (cdr l2) (cdr l1))
ang (angle pt1 pt2)
an1 (dtr (car l1))
an2 (dtr (car l2))
) ;_ setq
(if (numberp (cadr l3))
(add-polyline
modelspace
ins
(apply '(lambda (l1 l2) (append l1 (reverse l2)))
(mapcar
'(lambda (p a)
(mapcar
'(lambda (o) (mapcar '+ p o))
(mapcar
'(lambda (f) (mapcar 'f (off a)))
(list - +)
) ;_ mapcar
) ;_ mapcar
) ;_ lambda
(list pt1 pt2)
(list an1 an2)
) ;_ mapcar
) ;_ apply
T
) ;_ add-polyline
(plineobj->solidobj
(put-normal-to-path
(vla-addCircle modelspace (3dpt (cdr l1)) (/ (car l3) 2.0))
ins
(setq pth
(vla-addline modelspace (3dpt (cdr l1)) (3dpt (cdr l2)))
) ;_ setq
) ;_ put-normal-to-path
pth
T
) ;_ plineobj->solidobj
) ;_ if
) ;_ defun offset@angle
;;; returns a list of vertices given a line object
(defun line-obj->vert-lst (obj / name ret add-z)
(defun add-z (p) (append p '(0.0)))
(setq name (vla-get-objectname obj))
(cond
( (= name "AcDbLine")
nil
(mapcar
'(lambda (f)
(safearray-value (vlax-variant-value ((eval f) obj)))
) ;_ lambda
'(vla-get-startpoint vla-get-endpoint)
) ;_ mapcar
) ;_ condif
( (member name '("AcDbPolyline" "AcDb3dPolyline"))
(setq ret
(vlax-safearray->list
(vlax-variant-value (vla-get-Coordinates obj))
) ;_ vlax-safearray->list
) ;_ setq
(setq ret
(if (= name "AcDbPolyline")
(mapcar 'add-z (2-item-list ret))
(3-item-list ret)
) ;_ if
) ;_ setq
) ;_ condif
( (= T)
(error
"line-obj->vert-lst pass must be: line, pline, or 3dpline"
) ;_ error
(defun-q
line-obj->vert-lst (line / name ret add-z)
"Returns the list of vertices comprising LINE
LINE can be a Line, Polyline or 3DPolyline"
(declare (vars (line vla-object (member (vla-get-objectname line)
'("AcDbLine" "AcDbPolyline" "AcDb3dPolyline")))))
(defun add-z (p) (append p '(0.0)))
(setq name (vla-get-objectname line))
(cond
( (= name "AcDbLine")
nil
(mapcar
'(lambda (f)
(safearray-value (vlax-variant-value ((eval f) line)))
) ;_ lambda
'(vla-get-startpoint vla-get-endpoint)
) ;_ mapcar
) ;_ condif
) ;_ cond
) ;_ line-obj->vert-lst
( (member name '("AcDbPolyline" "AcDb3dPolyline"))
(setq ret
(vlax-safearray->list
(vlax-variant-value (vla-get-Coordinates line))
) ;_ vlax-safearray->list
) ;_ setq
(setq ret
(if (= name "AcDbPolyline")
(mapcar 'add-z (2-item-list ret))
(3-item-list ret))))))
(defun-r 'line-obj->vert-lst)

@ -1,38 +1,59 @@
;; return extents of a table row in the form '(top-left-pt bot-right-pt)
(defun table-row-extents (tableObj row / tl br tmp)
(set-file-docstring
"Table-handling functions
Some functions here have side effects such as modifying text in MLEADER callouts to match
the table. This is primarily fab-oriented but, with care, can be used for any table
operations.")
(defun-q
table-p (obj)
"Returns T if OBJ is a table"
(= "AcDbTable" (vla-get-ObjectName obj)))
(defun-r 'table-p)
(defun-q
table-row-extents (table row / tl br tmp)
"Returns the extents of a row in TABLE
The return value is a 2-item list in the form '(top-left-pt bot-right-pt)."
(declare (vars (table vla-object (table-p table))
(row int)))
(setq tmp (vlax-safearray->list
(vlax-variant-value (vla-GetCellExtents tableObj row 0 :vlax-false)))
(vlax-variant-value (vla-GetCellExtents table row 0 :vlax-false)))
tl (list (car tmp)
(cadr tmp)
(caddr tmp))
tmp (- (vla-get-Columns tableObj) 1)
tmp (- (vla-get-Columns table) 1)
tmp (vlax-safearray->list
(vlax-variant-value (vla-GetCellExtents tableObj row tmp :vlax-false))))
(vlax-variant-value (vla-GetCellExtents table row tmp :vlax-false))))
(repeat 3 (setq tmp (cdddr tmp)))
(setq br (list (car tmp)
(cadr tmp)
(caddr tmp)))
(list tl br))
(defun-r 'table-row-extents)
;; return the vla-object of a table with a given title (case-insensitive)
;; (uses wcmatch for checking)
(defun get-table-by-title (title / ret)
(defun-q
get-table-by-title (title / ret)
"Returns the table with matching TITLE
TITLE is matched against cell A1 of all tables in the database using wcmatch, so any
wildcards wcmatch accepts will work here."
(declare (vars (title str)))
(setq title (lowercase title))
(vlax-for ent modelSpace
(if (and (= (vla-get-objectname ent) "AcDbTable")
(wcmatch (lowercase (vla-GetText ent 0 0)) title))
(setq ret ent)))
ret)
;; prompt for a point and return the selected row in a table
;; point outside available rows = nil
(defun table-get-row (table
/
pt
row
last-row
ret
)
ret)
(defun-r 'get-table-by-title)
(defun-q
table-get-row (table / pt row last-row ret)
"Prompts user for a point and checks if it is inside a row in TABLE
If so, it returns the index of that row."
(declare (vars (table vla-object (table-p table))))
(osnap-off)
(setq pt (getpoint "\nSelect table row: ")
row 1
@ -43,20 +64,18 @@
(if (apply 'within-box-p (cons pt (table-row-extents table row)))
(setq ret row)))
ret)
(defun-r 'table-get-row)
;; insert rows in a fab table, before a certain row, and move the
;; others down to accommodate
(defun table-insert-rows (table nrows before
/
title
rowht
tables2move
lastrow
)
(defun-q
table-insert-rows (table nrows before / title rowht tables2move lastrow)
"Inserts NROWS rows in a fab drawing TABLE before row BEFORE
If there are other fab tables below this one, move them down to accomodate."
(declare (vars (table vla-object (table-p table))
(nrows int)
(before int)))
(setq rowht (vla-GetRowHeight table 0)
title (vla-GetText table 0 0))
(cond ((wcmatch title "Material")
(setq tbls2move '("Hardware"
"Assembly*"
@ -66,16 +85,27 @@
"Finish")))
((wcmatch title "Assembly*")
(setq tbls2move '("Finish"))))
(mapcar! 'get-table-by-title 'tbls2move)
(foreach tbl (strip-nil tbls2move)
(vla-Move tbl
(vlax-3d-point 0 0 0)
(vlax-3d-point 0 (- (* rowht nrows)) 0)))
(vla-InsertRows table before rowht nrows))
(defun-r 'table-insert-rows)
(defun-q
mat-table-update-marks (+- n at / prefix row rows mark marks)
"Update fab mark numbers in material table and callouts
;; update mark numbers
(defun mat-table-update-marks (+- n at / prefix row rows mark marks)
+- should be one of the built-in functions + or -, indicating whether the mark numbers
should increase or decrease. N is the number of places the number should jump. AT is the
first row in the material table containing mark numbers to change.
Expects to be called by a function with TABLE set to the material table."
(declare (vars (+- subr (member +- (list + -)))
(n int)
(at int)
(table vla-object (table-p table))))
(defun new-mark (mark)
(strcat prefix
(to-string (+- (atoi (vl-string-left-trim prefix mark)) n))))
@ -95,9 +125,17 @@
(mapcar '(lambda(x) (vla-replace-string modelSpace (car x) (cadr x)))
((if (= +- +) reverse identity) marks))
(vla-Regen acadDoc acAllViewPorts))
(defun-r 'mat-table-update-marks)
(defun-q
mat-table-add-data (before datas / n row)
"Adds bulk data to material table
;; add a bunch of rows to material table at once
(defun mat-table-add-data (before datas / n row)
BEFORE is the row to add data at. The special symbol 'end means the last row. DATAS is a
list of data lists representing row data."
(declare (vars (before nil (or (= before 'end)
(= (type before) 'int)))
(datas list (vl-every 'data-list-p datas))))
(if (= before 'end)
(setq before (1- (vla-get-Rows mat-table))))
(setq n (length datas)
@ -108,15 +146,16 @@
(mat-table-set-row mat-table row data)
(inc! 'row))
(vla-put-RegenerateTableSuppressed mat-table :vlax-false))
(defun-r 'mat-table-add-data)
;; write the cell data for the given row in the material table
(defun mat-table-set-row (mat-table row data
/
cols
row-ht
last-row
i
)
(defun-q
mat-table-set-row (mat-table row data / cols row-ht last-row i)
"Sets a ROW in MAT-TABLE
ROW is a row index and DATA is a data list whose keys correspond to the table columns."
(declare (vars (mat-table vla-object (table-p mat-table))
(row int (>= row 0))
(data nil (data-list-p data))))
(setq row-ht (vla-GetRowHeight mat-table 0)
last-row (1- (vla-get-Rows mat-table))
cols '(mark qty desc width length weight note))
@ -154,9 +193,12 @@
;; update weight sums
(vla-SetText mat-table last-row 4 (strcat "=sum(F3:F" (to-string last-row) ")"))
(mat-table-update-ea-wt mat-table))
(defun-r 'mat-table-set-row)
;; update the formula for "each weight" in the material table
(defun mat-table-update-ea-wt (mat-table / at lastrow formula)
(defun-q
mat-table-update-ea-wt (mat-table / at lastrow formula)
"Updates the formula for \"each weight\" in MAT-TABLE to include any added rows"
(declare (vars (mat-table vla-object (table-p mat-table))))
(setq at (get-table-by-title "Assembly*")
lastrow (1- (vla-get-Rows mat-table))
formula (strcat "=E"
@ -166,45 +208,46 @@
(vla-SetCellFormat mat-table lastrow 6 "%lu2%pr1")
(vla-SetText mat-table lastrow 6 formula)
(vla-SetRowHeight mat-table lastrow row-ht))
(defun-r 'mat-table-update-ea-wt)
(defun-q
table-multiply-qty (table qty-col n / i lastrow qty row-ht)
"Multiplies every quantity in TABLE by N
;; multiply every quantity (at qty-col) in the table by n
(defun table-multiply-qty (table qty-col n / i lastrow qty row-ht)
QTY-COL is the index of the quantities column."
(declare (vars (table vla-object (table-p table))
(qty-col int (>= qty-col 0))
(n int)))
(setq i 1
lastrow (vla-get-Rows table))
(while (< (inc! 'i) lastrow)
(setq qty (atoi (vla-GetText table i qty-col)))
(if (> qty 0) (vla-SetText table i qty-col (to-string (* qty n))))))
(if (> qty 0) (vla-SetText table i qty-col (to-string (* qty n))))))
(defun-r 'table-multiply-qty)
;; divide every quantity (at qty-col) in the table by n
(defun table-divide-qty (table qty-col n / i lastrow qty)
(defun-q
table-divide-qty (table qty-col n / i lastrow qty)
"Divides every quantity in TABLE by N
QTY-COL is the index of the quantities column."
(declare (vars (table vla-object (table-p table))
(qty-col int (>= qty-col 0))
(n int)))
(setq i 1
lastrow (vla-get-Rows table))
(while (< (inc! 'i) lastrow)
(setq qty (atoi (vla-GetText table i qty-col)))
(if (> qty 0) (vla-SetText table i qty-col (to-string (/ qty n))))))
;; prompt for a point and return the selected row in the hardware table
;; no hardware table = error
;; point outside available rows = nil
(defun hdw-table-get-row (hdw-table
/
pt
row
last-row
ret
)
(osnap-off)
(setq pt (getpoint "\nSelect hardware table row: ")
row 1
last-row (- (vla-get-Rows hdw-table) 1))
(osnap-on)
(while (<= (setq row (1+ row)) last-row)
(if (apply 'within-box-p (cons pt (table-row-extents hdw-table row)))
(setq ret row)))
ret)
(if (> qty 0) (vla-SetText table i qty-col (to-string (/ qty n))))))
(defun-r 'table-divide-qty)
;; write the cell data for the given row in the hardware table
(defun hdw-table-set-row (hdw-table row data / cols row-ht)
(defun-q
hdw-table-set-row (hdw-table row data / cols row-ht)
"Sets a ROW in HDW-TABLE
ROW is a row index and DATA is a data list whose keys correspond to the table columns."
(declare (vars (hdw-table vla-object (table-p hdw-table))
(row int (>= row 0))
(data nil (data-list-p data))))
(setq row-ht (vla-GetRowHeight hdw-table 0)
cols '(qty desc note))
;; unlock description
@ -220,10 +263,17 @@
(vla-SetRowHeight hdw-table row row-ht)
;; lock description
(vla-SetCellState hdw-table row 1 acCellStateContentLocked))
(defun-r 'hdw-table-set-row)
;; write the cell data for the given row in the generic ship loose template
(defun gsl-mat-table-set-row (gsl-mat-table row data / cols row-ht)
(debug-print-vars '(row data))
(defun-q
gsl-mat-table-set-row (gsl-mat-table row data / cols row-ht)
"Sets a ROW in a generic ship loose template table
ROW is a row index and DATA is a data list whose keys correspond to the table columns."
(declare (vars (gsl-mat-table vla-object (table-p gsl-mat-table))
(row int (>= row 0))
(data nil (data-list-p data))))
(setq cols '(mark qty desc width length note))
(foreach col cols
(if (setq col (assoc (lowercase (to-string col)) data))
@ -231,16 +281,15 @@
row
(+ 4 (vl-position (read (car col)) cols))
(cadr col)))))
(defun-r 'gsl-mat-table-set-row)
(defun-q
parse-table (table / row last-row col last-col line ret)
"Returns the data in TABLE as a nested list of strings
;; return the table data
(defun parse-table (table /
row
last-row
col
last-col
line
ret
)
Skips the first two rows (assumes they are headers) and any rows whose first column is
blank."
(declare (vars (table vla-object (table-p table))))
(setq row 1
last-row (- (vla-get-Rows table) 1)
col -1
@ -252,3 +301,4 @@
(setq col -1
line nil))
ret)
(defun-r 'parse-table)

@ -1,5 +1,12 @@
;; find and replace text in drawing
(defun vla-replace-string (block from to / name string)
(set-file-docstring
"Functions pertaining to text, mtext, leader, and mleader objects")
(defun-q
vla-replace-string (block from to / name string)
"Replaces FROM with TO in all text, mtext, and mleaders in BLOCK"
(declare (vars (block vla-object)
(from str)
(to str)))
(vlax-for ent block
(setq name (vla-get-ObjectName ent))
(cond ((= name "AcDbBlockReference")
@ -13,18 +20,37 @@
(if (wcmatch string (strcat "*" from "*"))
(vla-put-TextString ent
(vl-string-subst to from string)))))))
(defun-r 'vla-replace-string)
(defun-q
add-mtext (container ins text width attachmentpoint / obj)
"Wrapper for vla-AddMText
;; wrapper function for adding MText objects
(defun add-mtext (container ins text width attachmentpoint / obj)
ATTACHMENTPOINT should be a symbol like 'middlecenter or 'topleft."
(declare (vars (container vla-object)
(ins list (point-p ins))
(text str)
(width nil (numberp width))
(attachmentpoint sym)))
(setq obj (vla-AddMText container (3dpt 0 0 0) width text))
(vla-put-AttachmentPoint
obj
(vl-symbol-value (read (strcat "acAttachmentPoint" (to-string attachmentpoint)))))
(vla-put-InsertionPoint obj (3dpt ins))
obj)
(defun-r 'add-mtext)
(defun-q
add-text (container ins text height width alignment / obj)
"Wrapper for vla-AddMText
;; wrapper function for adding Text objects
(defun add-text (container ins text height width alignment / obj)
ALIGNMENT should be a symbol like 'middlecenter or 'topleft."
(declare (vars (container vla-object)
(ins list (point-p ins))
(text str)
(height nil (numberp height))
(width nil (numberp width))
(alignment sym)))
(setq obj (vla-AddText container text (3dpt ins) height))
(vla-put-Alignment
obj
@ -34,10 +60,17 @@
(vla-put-TextAlignmentPoint obj (3dpt ins)))
(vla-put-ScaleFactor obj width)
obj)
(defun-r 'add-text)
;; wrapper function for adding MLeader objects
;; points are relative to ins
(defun add-mleader (container ins points text / obj)
(defun-q
add-mleader (container ins points text / obj)
"Wrapper for vla-AddMLeader
POINTS is a list of leader points which are relative to INS"
(declare (vars (container vla-object)
(ins list (point-p list))
(points list (vl-every 'point-p points))
(text str)))
(mapcar! '(lambda(lst) (list (+ (car lst) (car ins))
(+ (cadr lst) (cadr ins))
(+ (last lst) (last ins))))
@ -52,4 +85,4 @@
(vla-setleaderlinevertices obj 0 (vlist->safearray points))
(vla-put-TextJustify obj acAttachmentPointMiddleRight)))
obj)
(defun-r 'add-mleader)

@ -1,6 +1,18 @@
;; get a selection of stairs and landings (enames),
;; optionally sorted in ascending order
(defun get-stairland (selection sort-p / filter ss enames)
(set-file-docstring
"Functions related to user selection")
(defun-q
get-stairland (selection sort-p / filter ss enames)
"Gets a selection of stairs and landings from the user
Returns enames of selected objects. SELECTION should be one of the following symbols:
- 'stair
- 'land
- 'both
SORT-P being non-nil means to sort selection set using %sort-stairland."
(declare (vars (selection sym (member selection '(stair land both)))))
(setq filter (list
(list -3
(list
@ -20,103 +32,119 @@
(if (and enames sort-p)
(vl-sort enames 'sort-stairland)
enames))
(defun-r 'get-stairland)
;;; similar to get-stairland but passes the list for filter no option
;;; for sorting, returns a list of entities matching xdata filter pass
;;; example: (get-sset '("Guard,3D_Guard")) will return a list of
;;; entities with xdata type matching Guard or 3D_Guard
(defun get-sset (lst / filter ss enames)
(setq filter (list (list -3 lst)))
(if (setq ss (ssget "I" filter))
(defun-q
get-sset (app-ids-str / filter ss enames)
"Gets a selection from the user of objects with given XDATA app-ID"
(setq filter (list (list -3 app-ids-str)))
(if (setq ss (ssget "I" filter))
(progn (setq enames (ss->lst ss)) (sssetfirst nil nil))
(if (setq ss (ssget filter)) (setq enames (ss->lst ss)))
) ;_ if
enames
) ;_ defun get-guard
(if (setq ss (ssget filter)) (setq enames (ss->lst ss))))
enames)
(defun-r 'get-sset)
;; convert a selection set to a list of enames
(defun ss->lst (ss / i ents)
(defun-q
ss->lst (ss / i ents)
"Returns selection set SS as a list of enames"
(setq i -1)
(repeat (sslength ss)
(add-to-list 'ents (ssname ss (setq i (1+ i)))))
ents)
(defun-r 'ss->lst)
;; delete the entity and return its former insertion point
(defun get-ins-and-delete (ename / blkIns-p ins)
;;; checks that passed ename is a block insert
(defun blkIns-p (ename)
(= "INSERT" (cdr (assoc 0 (entget ename))))
) ;_ defun blkIns-p
(chkargs "get-ins-and-delete" '((ename ename (blkIns-p))))
(defun-q
get-ins-and-delete (ename / blkIns-p ins)
"Deletes the entity named ENAME and returns its former insertion point"
(declare (vars (ename ename)))
(setq ins (cdr (assoc 10 (entget ename))))
;;; (if (begins-with (cdr (assoc 2 (entget ename))) "*U")
;;; (entdel ename)
;;; (delete&purge ename)
;;; ) ;_ if
(entdel ename)
ins
) ;_ defun get-ins-and-delete
;; use entsel and convert to vla object
(defun vlasel () (ename>vlobj (car (entsel))))
;; wrapper for getkword that uses defaults
;; ig-args are passed to initget
;; def-sym is a symbol pointing to a global value
;; prompt-str is used to build the getkword prompt
;; NOTE: calling function needs to make match-last-p symbol local if
;; that functionality is used
(defun getdkword (ig-args def-sym prompt-str / def-val kwords result)
(chkargs "getdkword" '((ig-args (list) ((= (length ig-args) 2)
(= (type (car ig-args)) 'int)
(stringp (last ig-args))))
(def-sym (sym))
(prompt-str (str))))
ins)
(defun-r 'get-ins-and-delete)
(defun-q
vlasel ()
"Alias for (ename>vlobj (car (entsel)))"
((lambda (ent) (if ent (ename>vlobj ent))) (car (entsel))))
(defun-r 'vlasel)
(defun-q
getdkword (ig-args def-sym prompt-str / def-val kwords result)
"Wrapper for initget/getkword that uses default values and remembers the last choice
IG-ARGS is a 2-item list of args to initget. The first element should be an int and the
second should be the list of keywords as a string. DEF-SYM is a symbol pointing to the
variable that holds the default value. PROMPT-STR is the prompt string, to which a leading
newline and the bracketed prompt items are added automatically.
If the keyword list contains a variant of \"matchlast\" (case-insensitive, so the caller
can determine the matching key sequence), the user may enter that keyword to skip
remaining prompts. This works by checking the value of the MATCH-LAST-P variable, which
should be set local in the calling command if used."
(declare (vars (ig-args (list)
(= (length ig-args) 2)
(= (type (car ig-args)) 'int)
(stringp (last ig-args)))
(def-sym (sym))
(prompt-str (str))))
(apply 'initget ig-args)
(setq def-val (eval def-sym)
kwords (string-subst-all "/" " " (last ig-args))
result (if match-last-p
def-val
(getkword (strcat "\n" prompt-str " [" kwords "] <" def-val ">: "))))
(if (= (lowercase result) "matchlast")
(if (and result
(= (lowercase result) "matchlast"))
(setq match-last-p T))
(if (and result
(/= (lowercase result) "matchlast"))
(set def-sym result)
(setq result def-val))
;; (vl-propagate def-sym)
result)
(defun-r 'getdkword)
(defun-q
getdint (def-sym prompt-str / def-val)
"Wrapper for getint that uses default values and remembers the last choice
;; wrapper for getint that uses defaults
(defun getdint (def-sym prompt-str / def-val)
(chkargs "getdint" '((def-sym (sym))
(prompt-str (str))))
See %getdkword for usage of DEF-SYM, PROMPT-STR, and MATCH-LAST-P."
(declare (vars (def-sym (sym))
(prompt-str (str))))
(setq def-val (vl-symbol-value def-sym))
(or* (list (if match-last-p
def-val
(getint (strcat "\n" prompt-str " <" (to-string def-val) ">: ")))
def-val)))
(defun-r 'getdint)
;; for selection filters
(defun ss-or (group-codes)
(defun-q
ss-or (group-codes)
"Returns a list of DXF codes wrapped in a DXF OR
Mainly for use in %dxf-filter."
(declare (vars (group-codes nil (listp group-codes))))
(append '((-4 . "<OR")) group-codes '((-4 . "OR>"))))
(defun ss-and (group-codes)
(defun-r 'ss-or)
(defun-q
ss-and (group-codes)
"Returns a list of DXF codes wrapped in a DXF AND
Mainly for use in %dxf-filter."
(declare (vars (group-codes nil (listp group-codes))))
(append '((-4 . "<AND")) group-codes '((-4 . "AND>"))))
(defun-r 'ss-and)
(defun-q
dxf-filter (defs / dxf-codes trans)
"Translates S-Expressions into DXF codes for passing to ssget
;; translate a more sensible format into DXF codes for passing to ssget
;; calls macro-expand on defs
;; Example:
;; (translate-dxf '((or (appid "Land") (and (type "DIMENSION") (text "") (angle #(dtr 90))))))
;; returns
;; ((-4 . "<OR")
;; (-3 ("Land"))
;; (-4 . "<AND")
;; (0 . "DIMENSION")
;; (1 . "")
;; (50 . 1.5708)
;; (-4 . "AND>")
;; (-4 . "OR>"))
(defun dxf-filter (defs / dxf-codes trans)
DEFS should be a list of group code S-Expressions. Note: %macro-expand is called on DEFS
before use. See source for which codes are available for use."
(declare (vars (defs nil (listp defs) (vl-every 'listp defs)))
(tests (equal (dxf-filter '((appid "Land"))) '((-3 ("Land"))))
(equal (dxf-filter '((and (type "CIRCLE") (layer "DIM"))))
'((-4 . "<AND") (0 . "CIRCLE") (8 . "DIM") (-4 . "AND>")))))
(setq dxf-codes '((appid -3)
(type 0)
(text 1)
@ -150,5 +178,4 @@
ret)
(flatten-to-cons-cells (mapcar 'trans (macro-expand defs))))
(defun-r 'dxf-filter)

@ -1,63 +1,185 @@
;; return true if str is a string
(defun stringp (str)
(= (type str) 'STR))
;; concatenate strings with a given delimiter
(defun strjoin (str-list delimiter)
(chkargs "strjoin" '((str-list list ((vl-every 'stringp str-list)))
(delimiter str)))
(set-file-docstring "String functions")
(defun-q
stringp (x)
"Returns nil if X is not string"
(= (type x) 'STR))
(declare-late 'stringp
'((tests (stringp "hello")
(not (stringp 1)))))
(defun-r 'stringp)
(defun-q
strjoin (str-list delimiter)
"Concatenates strings in STR-LIST with DELIMITER
Example:
(strjoin '(\"hello\" \"world\") \", \")
returns the string \"hello, world\"."
(vl-string-right-trim delimiter
(apply 'strcat
(mapcar '(lambda (str) (strcat str delimiter))
str-list))))
;; split strings on a given delimiter
(defun strsplit (str delimiter / return loc i next-word)
(chkargs "strsplit" '((str str)
(delimiter str)))
(declare-late 'strjoin
'((vars (str-list nil (listp str-list)
(vl-every 'stringp str-list))
(delimiter str))
(tests (= (strjoin '("key" "val") "=") "key=val")
(= (strjoin '("hello" "world") ", ") "hello, world"))))
(defun-r 'strjoin)
(defun-q
strsplit (str delimiter / dlen return loc i next-word)
"Splits STR on a DELIMITER
Example:
(strsplit \"hello, world\" \", \")
returns the list (\"hello\" \"world\")."
(defun next-word (loc i)
(substr str (1+ i) (- loc i)))
(setq return '()
i 0)
(while (setq loc (vl-string-position (ascii delimiter) str i))
(setq i 0
dlen (strlen delimiter))
(while (setq loc (vl-string-search delimiter str i))
(add-to-list 'return (next-word loc i))
(setq i (1+ loc)))
(setq i (+ loc dlen)))
(append return (list (next-word (strlen str) i))))
;; return a list of single character strings
(defun strbreak (str / return)
(chkargs "strbreak" '((str str)))
(setq return '())
(declare-late 'strsplit
'((vars (str str)
(delimiter str))
(tests (equal (strsplit "key=val" "=") '("key" "val"))
(equal (strsplit "hello, world" ", ") '("hello" "world")))))
(defun-r 'strsplit)
(defun-q
strbreak (str / return)
"Returns a list of single character strings comprising STR"
(foreach char (vl-string->list str)
(add-to-list 'return (chr char))))
;; returns T if pattern is at the beginning of str
(defun begins-with (str pattern / spec)
;; escape special characters for wcmatch
(setq spec "`#@.*?~[]-")
(foreach char (strbreak spec)
(setq pattern (vl-string-subst (strcat "`" char) char pattern)))
(wcmatch str (strcat pattern "*")))
(declare-late 'strbreak
'((vars (str str))
(tests (equal (strbreak "hi") '("h" "i"))
(= (strbreak "") nil))))
(defun-r 'strbreak)
;; returns T if pattern is at the end of str
(defun ends-with (str pattern / spec)
;; escape special characters for wcmatch
(defun-q
wcmatch-escape (str / spec)
"Escapes special characters in STR for use in wcmatch"
(setq spec "`#@.*?~[]-")
(foreach char (strbreak spec)
(setq pattern (vl-string-subst (strcat "`" char) char pattern)))
(wcmatch str (strcat "*" pattern)))
;; alias calls to strcase
(defun lowercase (str) (strcase (to-string str) T))
(defun uppercase (str) (strcase (to-string str) nil))
;; like vl-string-subst but substitutes all occurrences
(defun string-subst-all (new old string)
(chkargs "string-subst-all" '((new str)
(old str)
(string str)))
(while (vl-string-search old string)
(setq string (vl-string-subst new old string)))
string)
(setq str (vl-string-subst (strcat "`" char) char str))))
(declare-late 'wcmatch-escape
'((vars (str str))
(tests (= (wcmatch-escape "what # is 1*1?") "what `# is 1`*1`?")
(= (wcmatch-escape "backtick[`]@my-mail.com~")
"backtick`[```]`@my`-mail`.com`~"))))
(defun-r 'wcmatch-escape)
(defun-q
begins-with (str pattern / spec)
"Returns nil unless STR begins with PATTERN"
(wcmatch str (strcat (wcmatch-escape pattern) "*")))
(declare-late 'begins-with
'((vars (str str)
(pattern str))
(tests (begins-with "hello" "h")
(begins-with "hello" "")
(not (begins-with "hello" "a")))))
(defun-r 'begins-with)
(defun-q
ends-with (str pattern / spec)
"Returns nil unless STR ends with PATTERN"
(wcmatch str (strcat "*" (wcmatch-escape pattern))))
(declare-late 'ends-with
'((vars (str str)
(pattern str))
(tests (ends-with "hello" "o")
(ends-with "hello" "")
(not (ends-with "hello" "a")))))
(defun-r 'ends-with)
(defun-q
lowercase (str)
"Returns STR in lowercase"
(strcase (to-string str) T))
(declare-late 'lowercase
'((vars (str str))
(tests (= (lowercase "hi") "hi")
(= (lowercase "HI") "hi"))))
(defun-r 'lowercase)
(defun-q
uppercase (str)
"Returns STR in uppercase"
(strcase (to-string str) nil))
(declare-late 'uppercase
'((vars (str str))
(tests (= (uppercase "HI") "HI")
(= (uppercase "hi") "HI"))))
(defun-r 'uppercase)
(defun-q
string-subst-all (new old str / loc)
"Uses vl-string-subst repeatedly to substitute all occurrences of OLD for NEW in STR"
(if (= str "")
""
(progn
(setq loc (- (strlen new)))
(while (setq loc (vl-string-search old str (+ loc (strlen new))))
(setq str (vl-string-subst new old str loc)))
str)))
(declare-late 'string-subst-all
'((vars (new str)
(old str)
(str str))
(tests (= (string-subst-all "hello" "hi" "hi hi") "hello hello"))))
(defun-r 'string-subst-all)
(defun-q
first-line (str)
"Returns the first line of STR"
(car (strsplit str "\n")))
(declare-late 'first-line
'((vars (str str))
(tests (= (first-line "hi") "hi")
(= (first-line "item1\nitem2") "item1"))))
(defun-r 'first-line)
(defun-q
escape-quotes (str)
"Returns STR with double-quotes escaped"
(string-subst-all "\\\"" "\"" str))
;; testing this function does weird things to docstrings in HTML build
;; best to leave it out
(declare-late 'escape-quotes
'((vars (str str))))
(defun-r 'escape-quotes)
(defun-q
escape-newlines (str)
"Returns STR with newlines escaped"
(string-subst-all "\\n" "\n" str))
(declare-late 'escape-newlines
'((vars (str str))
(tests (= (escape-newlines "hi") "hi")
(= (escape-newlines "item1\nitem2") "item1\\nitem2"))))
(defun-r 'escape-newlines)

@ -1,24 +1,36 @@
(defun defined (x) (not (null x)))
(set-file-docstring "Symbol-handling functions")
;; switch two variables
(defun swap! (v1 v2 / tmp)
(defun-q
defined (x)
"Returns nil if X is null."
(not (null x)))
(defun-r 'defined)
(defun-q
swap! (v1 v2 / tmp)
"Swaps two variables."
(set 'tmp (vl-symbol-value v1))
(set v1 (vl-symbol-value v2))
(set v2 tmp))
(defun-r 'swap!)
(defun-q
symcat (str-lst)
"Concatenates strings in STR-LST and returns the value of the resulting symbol."
(vl-symbol-value (read (apply 'strcat (macro-expand str-lst)))))
(defun-r 'symcat)
(defun-q
macro-expand (expr-lst / eval-next)
"Return quoted list EXPR-LST with certain elements evaluated.
Macro-expand will evaluate elements of EXPR-LST which are prefaced by a #. Example:
;; concat strings to symbol and return its value
(defun symcat (lst)
(vl-symbol-value (read (apply 'strcat (macro-expand lst)))))
;; expand references to defined symbols in a list
;; replace a variable with #var
;; replace an expression with #(expression)
;; example:
;; (setq foo 4
;; bar 7)
;; (macro-expand '(+ 1 #foo #(+ #bar #foo 3)))
;; => '(+ 1 4 14)
(defun macro-expand (expr-lst / eval-next)
(setq foo 4
bar 7)
(macro-expand '(+ 1 #foo #(+ bar foo 3)))
will return the list '(+ 1 4 14)."
(foreach expr expr-lst
(if (= (type expr) 'LIST)
(if eval-next
@ -29,47 +41,109 @@
(if (= expr '#)
(setq eval-next T
expr-lst (remove-nth (vl-position '# expr-lst) expr-lst))
(if (begins-with (to-string (vl-symbol-name expr)) "#")
(subst! (eval (read (substr (to-string expr) 2)))
expr
'expr-lst)))))
((lambda(/ name)
(setq name (vl-symbol-name expr))
(if (begins-with name "#")
(subst! (eval (read (substr name 2)))
expr
'expr-lst)))))))
expr-lst)
(defun #! (expr-lst) (macro-expand expr-lst))
(defun-r 'macro-expand)
(defun-q
#! (expr-lst)
"Alias for %macro-expand"
(macro-expand expr-lst))
(defun-r '#!)
;; retuns true if sym is a symbol pointing to a list or nil
;; NOTE: do not call 'chkargs' inside type predicates
(defun sym-lst-p (sym)
(defun-q
sym-lst-p (sym)
"Returns nil if SYM is not a symbol or if its value is not a list"
(and (= (type sym) 'SYM)
(or (= (vl-symbol-value sym) nil)
(= (type (vl-symbol-value sym)) 'LIST))))
;; validate function arguments
;;
;; NOTE: do NOT call 'chkargs' inside type predicates or any other function used as a validator
;;
;; chkargs-func-name is a string naming the calling function
;; chkargs-args is a list whose length should be equal to the number of args the function accepts
;; For each item in 'chkargs-args',
;; [car] is the arg name as a symbol
;; [cadr] is the expected type, which may be a symbol or a list of symbols
;; [caddr] is optional. Each item is a validator to be run against [car]. This may be a symbol
;; pointing to a function which takes one argument or a list to eval. If a validator returns nil,
;; throw an error
(defun chkargs (chkargs-func-name
chkargs-args
/
chkargs-validators
chkargs-type
chkargs-name
chkargs-val
chkargs-idx
chkargs-call-lst
chkargs-header-string
chkargs-errors
chkargs-arg-errors
chkargs-error-idxs
chkargs-add-error
)
(listp (vl-symbol-value sym))))
(defun-r 'sym-lst-p)
(defun-q
sidekey (key / )
"Returns the value of the symbol named [SIDE]-[KEY]."
(declare (vars (side str)
(key str)))
(vl-symbol-value (read (strcat side "-" key))))
(defun-r 'sidekey)
(defun-q
sidekey-rail (key / )
"Returns the value of the symbol named [SIDE]-rail_[KEY]."
(declare (vars (side str)
(key str)))
(vl-symbol-value (read (strcat side "-rail_" key))))
(defun-r 'sidekey-rail)
(defun-q
tbkey (key)
"Returns the value of the symbol named [TB]_[KEY]."
(declare (vars (tb str)
(key str)))
(symcat '(#tb "_" #key)))
(defun-r 'tbkey)
(defun-q
fbkey (key)
"Returns the value of the symbol named [FB]_[KEY]."
(declare (vars (fb str)
(key str)))
(symcat '(#fb "_" #key)))
(defun-r 'fbkey)
(defun-q
lrkey (key)
"Returns the value of the symbol named [LR]_[KEY]."
(declare (vars (lr str)
(key str)))
(symcat '(#lr "_" #key)))
(defun-r 'lrkey)
(defun-q
cornerkey (key)
"Returns the value of the symbol named [CORNER]_[KEY]."
(declare (vars (corner str)
(key str)))
(symcat '(#corner "_" #key)))
(defun-r 'cornerkey)
(defun-q
chkargs (chkargs-func-name
chkargs-args
/
chkargs-validators
chkargs-type
chkargs-name
chkargs-val
chkargs-idx
chkargs-call-lst
chkargs-header-string
chkargs-errors
chkargs-arg-errors
chkargs-error-idxs
chkargs-add-error
)
"Validates arguments in the current function.
See %defun-r--process-declare-vars for a simplified way to invoke this function.
CHKARGS-FUNC-NAME should be the current function's name as a string. It is used for
reporting errors.
CHKARGS-ARGS should be an argument spec. Its length should be equal to the number of
arguments this function takes. At minimum, CHKARGS-ARGS should be a list of argument names
as symbols.
For each item in CHKARGS-ARGS:
[car] is the argument name as a symbol.
[cadr] is the expected type, which may be a symbol or a list of symbols
[caddr] is an optional list. Each item is a validator to be run against [car]. This may be
a symbol pointing to a function which takes one argument or a list to eval. If a validator
returns nil, throw an error."
(defun chkargs-call-lst (error-idxs / lst)
(setq lst (mapcar 'to-string (mapcar 'car chkargs-args)))
(foreach i (uniquify error-idxs)
@ -120,17 +194,11 @@
(if chkargs-errors (error (strcat "Argument error(s):\n"
(chkargs-call-lst chkargs-error-idxs)
(apply 'strcat chkargs-errors)))))
(defun-r 'chkargs)
;; helper functions that use global variables
(defun sidekey (key / )
(vl-symbol-value (read (strcat side "-" key))))
(defun sidekey-rail (key / )
(vl-symbol-value (read (strcat side "-rail_" key))))
(defun tbkey (key)
(symcat '(#tb "_" #key)))
(defun fbkey (key)
(symcat '(#fb "_" #key)))
(defun lrkey (key)
(symcat '(#lr "_" #key)))
(defun cornerkey (key)
(symcat '(#corner "_" #key)))
(defun-q
propagate (var-sym value)
"Sets VAR-SYM to VALUE and calls vl-propagate"
(set var-sym value)
(vl-propagate var-sym))
(defun-r 'propagate)

@ -0,0 +1,87 @@
(set-file-docstring
"Functions related to unit testing")
(defun-q
defun-t (test-sym / test-body test-args test-docstring)
"Registers function at TEST-SYM as a unit test.
Test functions should:
- take no arguments
- have short (1-line) docstrings describing what they test
- return a list of assertion errors using %assert-all
- be self contained
- NOT set any symbols in the global environment
Each test function will be wrapped with advice that reports failures by printing its name,
docstring, and errors (otherwise it returns nil). A call to the resulting function will be
saved to *file-tests* so %test-build-lsp can write them to tests.lsp. Then, TEST-SYM will
be set to nil to mark the original test function for garbage collection."
(if *test-build-p*
(progn
(setq test-body (defun-q-list-ref test-sym)
test-args (pop! 'test-body)
test-docstring (pop! 'test-body))
(if (not (member '/ test-args))
(setq test-args (rcons test-args '/)))
(setq test-args (rcons test-args '*assert-return-error*)
test-body (cons '(setq *assert-return-error* t) test-body))
(add-to-alist
'*file-tests*
*file-name*
(list
(to-string
(#!
'((lambda (/ errors)
(setq errors (#(cons 'lambda (cons test-args test-body))))
(if errors
(progn
(princ #(strcat "\nTEST "
(lowercase (to-string test-sym))
": "
test-docstring "\n"))
(princ (strjoin errors "\n"))
(princ "\n")
(setq n-errors (+ n-errors (length errors))))))))))
t)))
(set test-sym nil))
(defun-r 'defun-t)
;; pause to process tests defined earlier
;; see beginning of acaddoc.lsp for early defun-t definition
(map-apply '(lambda(*file-name* f-sym) (defun-t f-sym))
*delayed-test*)
(setq *delayed-test* nil)
(defun-q
test-build ()
"Bootstraps test build process and opens a new blank drawing to carry it out."
(propagate '*test-build-p* t)
(vla-Activate (vla-Add (vla-get-Documents acadObj)))
(princ))
(defun-r 'test-build)
(defun-q
test-build-lsp (/ f)
"Writes tests.lsp with all the test forms in *file-tests*"
(setq f (open (strcat psc-src-dir "tests.lsp") "w"))
(foreach file *file-tests*
(princ (strcat "(psc-include (list \"" (car file) "\"))") f)
(foreach test (cdr file)
(princ (strcat "\n;;\n" (to-string test) "\n") f))
(princ "\n\n" f))
(princ "(princ)" f)
(close f))
(defun-r 'test-build-lsp)
(defun-q
test-run (/ n-errors)
"Alias for (load \"tests.lsp\")."
(setq n-errors 0)
(load "tests.lsp")
(if (= n-errors 0)
(princ "All tests passed!")
(princ (strcat (to-string n-errors) " errors")))
(princ))
(defun-r 'test-run)

@ -1,73 +1,60 @@
;; return current date
(defun curdate ( / cdate)
(set-file-docstring
"Time- and date-related functions")
(defun-q
curdate ( / cdate)
"Returns the current date formatted as DD/MM/YYYY"
(setq cdate (rtos (getvar 'cdate) 2 6))
(strcat (substr cdate 5 2)
"/"
(substr cdate 7 2)
"/"
(substr cdate 3 2)))
(defun-r 'curdate)
(defun-q
!time (func alert-p / t1 t2 dur str ret)
"Prints the number of milliseconds it takes to run FUNC
;; returns the number of seconds a function runs for
;; func - a function to perform
;; alert-p T or nil to determine alert
;; EXAMPLE (!TIME '(ENTGET (ENTLAST)) NIL)
(defun !time (func alert-p / t1 t2 dur str ret)
(chkargs
"!time"
'(
(func list ((function-p (eval (car func)))))
(alert-p nil)
)
) ;_ chkargs
(setq t1 (getvar 'millisecs))
(setq ret (eval func))
(setq t2 (getvar 'millisecs))
(setq dur (* 0.001 (- t2 t1)))
(setq str
(strcat
"\nThe duration of "
(vl-symbol-name (car func))
" was "
(rtos dur 2 2)
" seconds"
) ;_ strcat
) ;_ setq
FUNC should be a lisp form to be eval'd. If ALERT-P is nil, prints to the command
line. Otherwise, displays an alert box with the result.
Example: (!time '(repeat 100 (read-xdata (entlast) \"\")) nil)"
(declare (vars (func list (function-p (eval (car func))))))
(setq t1 (getvar 'millisecs)
ret (eval func)
t2 (getvar 'millisecs)
dur (rtos (* 0.001 (- t2 t1)) 2 2)
str (strcat "\nThe duration of " (vl-symbol-name (car func))
" was " dur " seconds"))
(if alert-p (alert str) (vla-prompt (vla-get-utility acaddoc) str))
ret
) ;_ defun
ret)
(defun-r '!time)
(defun-q
!avg-time (fun num / lst rt1 rt2 tot ms1 ms2 dur)
"Prints statistical results after calling %!time NUM times
;;; return the average number of seconds a function runs for
;;; fun - a function to repeat a given number of times
;;; num - the number of times to repeat the given function
;;; EXAMPLE (!AVG-TIME '(ENTGET (ENTLAST)) 100)
(defun !avg-time (fun num / lst rt1 rt2 tot ms1 ms2 dur)
(chkargs
"!avg-time"
'(
(fun list ((function-p (eval (car fun)))))
(num int)
)
) ;_ chkargs
(setq lst '(0))
(setq rt1 (getvar 'millisecs))
FUN is the first argument to %!time. This function passes nil for ALERT-P."
(declare (vars (fun list (function-p (eval (car fun))))
(num int)))
(setq lst '(0)
rt1 (getvar 'millisecs))
(repeat num
(setq ms1 (getvar 'millisecs))
(!time fun nil)
(setq ms2 (getvar 'millisecs))
(setq dur (* 0.001 (- ms2 ms1)))
(append! 'lst (list dur))
) ;_ repeat
(setq rt2 (getvar 'millisecs))
(setq tot (* 0.001 (- rt2 rt1)))
(setq ms2 (getvar 'millisecs)
dur (* 0.001 (- ms2 ms1)))
(append! 'lst (list dur)))
(setq rt2 (getvar 'millisecs)
tot (* 0.001 (- rt2 rt1)))
(if (and (apply 'and (mapcar 'numberp lst)) (setq lst (cdr lst)))
(progn
(print (strcat (vl-symbol-name (car fun)) " (" (itoa num) "x)"))
(print (strcat "min: " (rtos (apply 'min lst) 2 2)))
(print (strcat "max: " (rtos (apply 'max lst) 2 2)))
(print (strcat "avg: " (rtos (/ (apply '+ lst) (length lst)) 2 2)))
(print (strcat "tot: " (rtos tot 2 2)))
) ;_ progn
) ;_ if
(/ tot num)
) ;_ defun !avg-time
(print (strcat "tot: " (rtos tot 2 2)))))
(/ tot num))
(defun-r '!avg-time)

@ -1,3 +1,6 @@
(set-file-docstring
"XDATA-handling functions")
;; format a list to be applied to an entity as XDATA
(defun format-xdata (xdata_list / xdata_temp xd_type)
(setq xdata_list
@ -290,12 +293,11 @@
;; returns true if data is a data list
(defun data-list-p (data)
(or (and (= (type data) 'LIST)
(vl-every '(lambda(x)
(and (= (type x) 'LIST)
(= (type (car x)) 'STR)))
data))
(null data)))
(and (listp data)
(vl-every '(lambda(x)
(and (= (type x) 'LIST)
(= (type (car x)) 'STR)))
data)))
;; returns true if a data list contains the key
(defun key-in-p (key lst)
@ -324,3 +326,37 @@
'(lambda(x) (= (xd-value "assembly" x) assembly))
(mapcar '(lambda(e) (read-xdata e app))
(ss->lst (ssget "A" (#! '((-3 (#app)))))))))
(defun-q
subst-key (key new-val lst)
"Returns data list LST with value of KEY replaced with NEW-VAL"
(declare (vars (key str)
(lst nil (data-list-p lst)))
(tests (equal (subst-key 'a 2 '((a 1))) '((a 2)))
(equal (subst-key 'a 2 '((b 1))) '((b 1)))))
(subst (list key new-val) (assoc key lst) lst))
(defun-r 'subst-key)
(defun-q
subst-key! (key new-val lst-sym)
"Updates data list at LST-SYM in place using %subst-key"
(declare (vars (key str)
(lst-sym sym (data-list-p (vl-symbol-value lst-sym))))
(tests (equal ((lambda(lst) (subst-key! 'a 2 'lst) lst) '((a 1))) '((a 2)))
(equal ((lambda(lst) (subst-key! 'a 2 'lst) lst) '((b 1))) '((b 1)))))
(set lst-sym (subst-key key new-val (vl-symbol-value lst-sym))))
(defun-r 'subst-key!)
(defun-q
has-duplicate-keys-p (data / last-key ret)
"Returns T if any keys in data list DATA are repeated"
(declare (vars (data nil (data-list-p data)))
(tests (has-duplicate-keys-p '(("a" 1)("b" 2)("a" 3)))
(not (has-duplicate-keys-p '(("a" 1)("b" 2))))))
(sort! 'data '(lambda(a b) (< (car a) (car b))))
(foreach elt data
(if (= (car elt) last-key) (setq ret T))
(setq last-key (car elt)))
ret)
(defun-r 'has-duplicate-keys-p)

Loading…
Cancel
Save