R5RS Scheme Wavefront .obj (3d Files)

This library will allow for the easy creation of 3d Wavefront .Obj Files https://en.wikipedia.org/wiki/Wavefront_.obj_file in Scheme. I’ve made sure the code is R5RS compliant, tested under Dr-Racket but I’m pretty sure it should work in MIT-scheme and probably many other dialects.
There’s not a huge amount to it, it will require that you first download R5RS Scheme Essential Functions, and these few vector definitions saved as Vectors.scm

;;3vectors
(define (normalize_3vector v)
 (let ((mag (3vector_magnitude v))) (3vector (/ (3V_x v) mag) (/ (3V_y v) mag) (/ (3V_z v) mag))))
(define (3vector x y z) 
(cons x (cons y z))) (define (3V_x v) (car v)) (define (3V_y v) (cadr v)) (define (3V_z v) (cddr v))
(define (3vector_magnitude v) 
(sqrt (+ (expt (3V_x v) 2) (expt (3V_y v) 2) (expt (3V_z v) 2))))
(define (vectormap arity function v1 v2) (if (= 1 arity)
                                             (function v1 v2)
                                             (cons (function (car v1) (car v2)) (vectormap (- arity 1) function (cdr v1) (cdr v2)))))
(define (vector_addition arity v1 v2)
 (vectormap arity + v1 v2))
(define (vector_subtraction arity v1 v2)
 (vectormap arity - v1 v2))
(define (3vector_addition v1 v2)
(vectormap 3 + v1 v2))

After that you should have no trouble creating windows files
To use it just call (.obj vertices faces).
-Each vertex is a 3vector. (vertices is a list of 3vectors. )
-Each face is a list of ordinal integers representing the position of the vertex in the prior list. (i.e. the face ‘(1 2 3) is triangle made from vertex 1, vertex 2, and vertex 3 in clockwise order)
-In the wavefront .obj format a face in anticlockwise means its only visible from the opposite side.
So you don’t have to think of this it will generate both sides of each face, which will double your file size, to turn this off set the final argument in the call to obj-face to False #f

obj.scm

(load "Vectors.scm")
(load "Essentials.scm")
(define (obj-vertex vertex)
  (string-append "v "
                 (number->string (3V_x vertex)) " "
                 (number->string (3V_y vertex)) " "
                 (number->string (3V_z vertex)) "\n"))

(define (obj-vertices vertices)
  (if (null? vertices)
      "\n"
      (string-append (obj-vertex (car vertices)) (obj-vertices (cdr vertices) ))))

(define (obj-face face bothsides) (if (= 4 (length face))
                                      (let ((face1 (list (car face) (cadr face) (caddr face)))
                                            (face2 (list (car face) (caddr face) (cadddr face))))
                                        (string-append (obj-triangle face1 bothsides) (obj-triangle face2 bothsides) ))
                                      (obj-triangle face bothsides)
                                      ))
(define (obj-triangle face bothsides)
  (string-append "f "
                 (number->string (car face)) " "
                 (number->string (cadr face)) " "
                 (number->string (caddr face)) "\n"
                 (if bothsides (string-append "f "
                 (number->string (caddr face)) " "
                 (number->string (cadr face)) " "
                 (number->string (car face)) "\n"
                 ) "")))

(define (obj-faces faces)
  (if (null? faces)
      "\n"
      (string-append (obj-face (car faces) #t) (obj-faces (cdr faces)))))

(define (.obj vertices faces) (string-append "#vertices \n" (obj-vertices vertices) "#faces \n"  (obj-faces faces)))

A good example of it working in action can be found here R5RS Scheme 3d Extruder

Back to top