#+TITLE: (sphere) csg lisp org
#+AUTHOR: screwtape
#+EMAIL:
[email protected]
* org tables to bubbly stereolithographs with gmsh geometry
If you need help getting it working give me a shout!
** Requirements are
emacs, orgmode, slime,
and having gmsh installed.
I'm just printing a gmsh geometry. gmsh does the work (probably using oce) ;
looked at with gmsh's fltk gui.
Pro tip: Turn on surfaces in gmsh's tools>Options geometry and mesh checkboxes.
** org-doc usage
1. Find or make a table like the ones in ** tables
2. Give that table to create's :var table=diamond
(with your table's #+name: instead of diamond)
3. Run (C-c C-c yes <ret>) with the cursor at the create lisp src block in
4. Run the mesh-and-show shell src block
Tangling to lisp/shell files also works fine. (C-c C-v t)
** Table format:
- (start-number . end-number) defines radius-overlapping bubbles in an obvious range.
- end-number fills (1 . end-number)
- Empty cells should be 0 (which would try to fill 1 to 0, and hence be eaten by nconc)
- Non-numeric cells are passed to lisp as strings, which then READs them. Caveat emptor.
- Just look at the tables and some pngs. (1pngs/superman.png and 1pngs/diamond.png)
** tables
*** superman s
#+name: superman
| 0 | 1 | 0 |
| 1 | 0 | 1 |
| 1 | 0 | 1 |
| 1 | 0 | 0 |
| 0 | 1 | 0 |
| 0 | 0 | 1 |
| 1 | 0 | 1 |
| 1 | 0 | 1 |
| 0 | 1 | 0 |
*** diamond
**** lisp
#+begin_src lisp :results value
(loop for x from 1 to 5 collecting
(loop for y from 1 to 5 collecting
(cond
((= x y 3) (cons 1 5))
((and (< x 5) (> x 1)
(< y 5) (> y 1))
(cons 2 4))
(t (cons 3 3)))))
#+end_src
#+RESULTS:
**** table
#+name: diamond
| (3 . 3) | (3 . 3) | (3 . 3) | (3 . 3) | (3 . 3) |
| (3 . 3) | (2 . 4) | (2 . 4) | (2 . 4) | (3 . 3) |
| (3 . 3) | (2 . 4) | (1 . 5) | (2 . 4) | (3 . 3) |
| (3 . 3) | (2 . 4) | (2 . 4) | (2 . 4) | (3 . 3) |
| (3 . 3) | (3 . 3) | (3 . 3) | (3 . 3) | (3 . 3) |
** create geometry
#+name: create
#+begin_src lisp :noweb yes :var table=diamond :results none
<<table2mesh>>
#+end_src
** Mesh and show with gmsh
#+name: mesh-and-show
#+begin_src shell :results none
gmsh -o test.stl fromtable.geo -3
gmsh test.stl
#+end_src
** Base
*** Tangled
#+name: table2mesh
#+begin_src lisp :results output :noweb yes :var table=table :eval never :results none
<<readornum>>
<<mesh>>
(flet ((mesh ()
(let* ((r 5) (lc 2) (table (read-table-strings))
(spheres
(loop for row in (read-table-strings) for y from 0 by r nconcing
(loop for col in row for x from 0 by r nconcing
(loop for height from (if (consp col) (car col) 1)
to (if (consp col) (cdr col) col)
for z from (* r height) by r
collecting (make-instance 'sphere :x x :y y :z z :r r :lc lc))))))
(when spheres
(geo-beginning (car spheres))
(mapc 'fill-forms spheres)
(with-open-file (*standard-output* #p"fromtable.geo" :direction :output
:if-exists :supersede
:if-does-not-exist :create)
(loop for sphere in spheres do
(mapc 'eval (forms sphere))))))))
(mesh))
#+end_src
*** READ strings leave numbers
#+name: readornum
#+begin_src lisp :noweb yes :results value :eval never
(defun read-table-strings (&optional (table table))
(loop for row in table collecting
(loop for col in row collecting
(if (stringp col) (with-input-from-string (s col) (read s))
col))))
#+end_src
*** mesh package / sphere template
#+name: mesh
#+begin_src lisp :eval never
(defpackage mesh (:use cl) (:export sphere geo-beginning fill-forms))
(in-package mesh)
(defclass 3d ()
((points :initform nil :accessor points)
(lc :initarg :lc :reader lc)
(complexes :initform nil :accessor complexes)
(phys :initform nil :accessor phys)
(surface :accessor surface)
(volume :accessor volume)
(next-point :allocation :class)
(next-phy :allocation :class)
(next-complex :allocation :class))
(:default-initargs :lc 5))
(defmethod new-pnt ((obj 3d)) "
(new-pnt 3D)
Adds a class-scope new point.
"
(let ((idx (incf (slot-value obj 'next-point))))
(if (null (points obj))
(setf (points obj) (list idx))
(nconc (points obj) (list idx)))
(values idx)))
(defmethod pof ((obj 3d) n) "
(pof 3d x)
Gets the locally scoped (1- x) indexed point
"
(nth (1- n) (points obj)))
(defmethod cof ((obj 3d) n) "
(cof 3d x)
Gets the locally scoped (1- x) indexed complex
"
(nth (1- n) (complexes obj)))
(defmethod new-phy ((obj 3d)) "
(new-phy 3d)
Adds a class-scope physical object
"
(let ((idx (incf (slot-value obj 'next-phy))))
(if (null (phys obj))
(setf (phys obj) (list idx))
(nconc (phys obj) (list idx)))
(values idx)))
(defmethod new-cpx ((obj 3d)) "
(new-cpx 3d)
Adds a class-scope complex object
"
(let ((idx (incf (slot-value obj 'next-complex))))
(if (null (complexes obj))
(setf (complexes obj) (list idx))
(nconc (complexes obj) (list idx)))
(values idx)))
(defmethod shared-initialize :after ((obj 3d) names &rest args)
(declare (ignore names args))
(unless (slot-boundp obj 'next-point)
(setf (slot-value obj 'next-point) 0
(slot-value obj 'next-complex) 0
(slot-value obj 'next-phy) 0)))
(defclass sphere (3d)
((x :initarg :x :accessor x)
(y :initarg :y :accessor y)
(z :initarg :z :accessor z)
(r :initarg :r :accessor r)
(sphere-forms :type list :initform (list) :accessor forms))
(:documentation "
(make-instance mesh:sphere :x 3 :y 4 :z 5 :r 10 :lc 3)
Instantiates an object for the printing of a gmsh geometry of
said sphere equivalent to gmsh's unit sphere
(but with class scope indices)
"))
(defun blank () '(format t "~%"))
(defmethod add-point ((obj 3d) x y z)
(let ((p (new-pnt obj)))
`(format t "Point(~d) = {~d,~d,~d,lc};~%" ,p ,x ,y ,z)))
(defmethod add-circle ((obj 3d) a b c &aux (p (pof obj a)) (q (pof obj b)) (r (pof obj c)))
(let ((s (new-cpx obj)))
`(format t "Circle(~d) = {~d,~d,~d};~%" ,s ,p ,q ,r)))
(defun sgn (n) (if (plusp n) 1 -1))
(defmethod add-curvel ((obj 3d) a b c &aux
(p (* (sgn a) (cof obj (abs a))))
(q (* (sgn b) (cof obj (abs b))))
(r (* (sgn c) (cof obj (abs c)))))
(let ((s (new-cpx obj)))
`(format t "Curve Loop(~d) = {~d,~d,~d};~%" ,s ,p ,q ,r)))
(defmethod add-surface ((obj 3d) x)
(let ((s (new-cpx obj)))
`(format t "Surface(~d) = {~d};~%" ,s ,(cof obj x))))
(defmethod add-volume ((obj 3d) x)
(let ((s (new-cpx obj)))
`(format t "Volume(~d) = {~d};~%" ,s ,(cof obj x))))
(defmethod add-surfl ((obj 3d) &rest args)
(let ((s (new-cpx obj)) (new-args (mapcar (lambda (x) (cof obj x)) args)))
`(format t "Surface Loop(~d) = {~@{~d~^,~}};~%" ,s ,@new-args)))
(defmethod phy-surf ((obj 3d) &rest args)
(let ((s (new-phy obj)) (new-args (mapcar (lambda (x) (cof obj x)) args)))
`(format t "Physical Surface(~d) = {~@{~d~^,~}};~%" ,s ,@new-args)))
(defmethod phy-vol ((obj 3d) vol)
(let ((s (new-phy obj)))
`(format t "Physical Volume(~d) = {~d};~%" ,s ,(cof obj vol))))
(defmethod geo-beginning ((obj sphere) &aux (mesh-algo 6) (lc (lc obj)))
(setf (forms obj)
(list `(format t "~a = ~d;~%" "Mesh.Algorithm" ,mesh-algo)
(blank)
`(format t "lc = ~d;~%" ,lc))))
(defmethod fill-forms ((obj sphere)
&aux (x (x obj)) (y (y obj)) (z (z obj))
(r (r obj)) (lc (lc obj))) "
(fill-forms sphere)
Prints the Gmsh geometry of a unit sphere at (x sphere) ..
of radius/lc (r sphere) (lc sphere)
Exactly equivalent to the the unit sphere from gmsh.
The point / extrusion / physical counters are class scope
so subsequent mesh::3d instances can be printed in the same
geometry. 'complexes' is an erroneous name.
"
(setf (forms obj)
(nconc (forms obj)
(list (add-point obj x y z)
(add-point obj (+ x r) y z)
(add-point obj x (+ y r) z)
(add-circle obj 2 1 3)
(add-point obj (- x r) y z)
(add-point obj x (- y r) z)
(add-circle obj 3 1 4) ; 10
(add-circle obj 4 1 5)
(add-circle obj 5 1 2)
(add-point obj x y (- z r))
(add-point obj x y (+ z r)) ; 14
(add-circle obj 3 1 6)
(add-circle obj 6 1 5)
(add-circle obj 5 1 7)
(add-circle obj 7 1 3)
(add-circle obj 2 1 7)
(add-circle obj 7 1 4)
(add-circle obj 4 1 6)
(add-circle obj 6 1 2) ; 22
(add-curvel obj 2 8 -10)
(add-surface obj 13)
(add-curvel obj 10 3 7)
(add-surface obj 15)
(add-curvel obj -8 -9 1)
(add-surface obj 17) ; 28
(add-curvel obj -11 -2 5)
(add-surface obj 19)
(add-curvel obj -5 -12 -1)
(add-surface obj 21)
(add-curvel obj -3 11 6)
(add-surface obj 23)
(add-curvel obj -7 4 9)
(add-surface obj 25)
(add-curvel obj -4 12 -6)
(add-surface obj 27) ; 38
(add-surfl obj 28 26 16 14 20 24 22 18)
(add-volume obj 29)
(blank)
(phy-surf obj 28 26 16 14 20 24 22 18)
(phy-vol obj 30)
(blank)))))
#+end_src